Prechádzať zdrojové kódy

Merge branch 'dev' into nb-pc-rabbitmq

# Conflicts:
#	nb-service/app-assistant/src/main/java/com/nb/app/assistant/service/LocalAssistantUserBindService.java
#	nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusPatientService.java
18339543638 3 rokov pred
rodič
commit
beb2b9de12
65 zmenil súbory, kde vykonal 1148 pridanie a 164 odobranie
  1. 15 4
      nb-admin/src/main/resources/application-dev.yml
  2. 15 4
      nb-admin/src/main/resources/application-prod.yml
  3. 3 1
      nb-admin/src/main/resources/application.yml
  4. 21 1
      nb-admin/src/test/java/com/nb/admin/BusClinicTest.java
  5. 19 0
      nb-admin/src/test/java/com/nb/admin/NotifyTest.java
  6. 1 0
      nb-auth/src/main/java/com/nb/auth/bean/LoginUser.java
  7. 2 1
      nb-common/delay-queue-common/src/main/java/com/nb/common/queue/delay/RedissonDelayMessageManager.java
  8. 1 1
      nb-im/src/main/java/com/nb/im/delay/ImRoomAutoFinishDelayHandler.java
  9. 8 1
      nb-im/src/main/java/com/nb/im/entity/ImRoomEntity.java
  10. 26 0
      nb-im/src/main/java/com/nb/im/event/ImFinishedEvent.java
  11. 27 4
      nb-im/src/main/java/com/nb/im/job/ImRoomJob.java
  12. 34 5
      nb-im/src/main/java/com/nb/im/listener/ImPatientInfoListener.java
  13. 32 21
      nb-im/src/main/java/com/nb/im/listener/MonitorStatusListener.java
  14. 30 4
      nb-im/src/main/java/com/nb/im/service/LocalImRoomService.java
  15. 5 1
      nb-oss/pom.xml
  16. 26 0
      nb-oss/src/main/java/com/nb/oss/strategy/AliyunOssProperties.java
  17. 63 0
      nb-oss/src/main/java/com/nb/oss/strategy/AliyunOssUtil.java
  18. 28 6
      nb-oss/src/main/java/com/nb/oss/strategy/MinioConfig.java
  19. 1 1
      nb-oss/src/main/java/com/nb/oss/strategy/MinioUtil.java
  20. 1 1
      nb-oss/src/main/java/com/nb/oss/strategy/doc/OssDocConfig.java
  21. 2 2
      nb-oss/src/main/java/com/nb/oss/strategy/impl/AbstractFileStorage.java
  22. 38 2
      nb-oss/src/main/java/com/nb/oss/strategy/impl/AliyunStorageStrategy.java
  23. 1 1
      nb-oss/src/main/java/com/nb/oss/strategy/impl/MinioStorageStrategy.java
  24. 3 0
      nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/entity/AssistantUserBindEntity.java
  25. 23 2
      nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/event/AssistLiftEvent.java
  26. 25 0
      nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/event/ImNoneEvent.java
  27. 25 0
      nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/event/ImSuccessEvent.java
  28. 12 1
      nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/feign/IAssistantUserBindClient.java
  29. 42 0
      nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/feign/result/PatientBindAssistantResult.java
  30. 3 0
      nb-service-api/app-msg-api/src/main/java/com/nb/app/msg/bean/MsgBean.java
  31. 3 1
      nb-service-api/app-msg-api/src/main/java/com/nb/app/msg/enums/MsgEnum.java
  32. 4 0
      nb-service/app-assistant/pom.xml
  33. 6 3
      nb-service/app-assistant/src/main/java/com/nb/app/assistant/auth/AssistantPasswordGranter.java
  34. 7 4
      nb-service/app-assistant/src/main/java/com/nb/app/assistant/auth/AssistantPhoneGranter.java
  35. 25 2
      nb-service/app-assistant/src/main/java/com/nb/app/assistant/controller/CaptchaController.java
  36. 14 6
      nb-service/app-assistant/src/main/java/com/nb/app/assistant/controller/PatientOperationController.java
  37. 26 0
      nb-service/app-assistant/src/main/java/com/nb/app/assistant/listener/AssistPatientInfoListener.java
  38. 0 1
      nb-service/app-assistant/src/main/java/com/nb/app/assistant/listener/ClinicManageInfoListener.java
  39. 6 0
      nb-service/app-assistant/src/main/java/com/nb/app/assistant/mapper/AssistantUserBindMapper.java
  40. 22 3
      nb-service/app-assistant/src/main/java/com/nb/app/assistant/service/LocalAssistantUserBindService.java
  41. 31 0
      nb-service/app-assistant/src/main/java/com/nb/app/assistant/sms/AliSmsClientConfig.java
  42. 48 0
      nb-service/app-assistant/src/main/java/com/nb/app/assistant/sms/SmsHelper.java
  43. 33 0
      nb-service/app-assistant/src/main/resources/mapper/AssistantUserBindMapper.xml
  44. 1 0
      nb-service/app-doctor/src/main/java/com/nb/app/doctor/auth/AppDoctorAuthGranter.java
  45. 13 3
      nb-service/app-doctor/src/main/java/com/nb/app/doctor/controller/NoticeMsgController.java
  46. 26 16
      nb-service/app-doctor/src/main/java/com/nb/app/doctor/controller/PatientMonitorController.java
  47. 1 1
      nb-service/app-doctor/src/main/java/com/nb/app/doctor/service/LocalAppDoctorUserService.java
  48. 85 0
      nb-service/app-doctor/src/main/java/com/nb/app/doctor/service/dto/AssistantUserBindResult.java
  49. 8 0
      nb-service/app-msg/pom.xml
  50. 20 2
      nb-service/app-msg/src/main/java/com/nb/app/msg/entity/AppMsgEntity.java
  51. 14 3
      nb-service/app-msg/src/main/java/com/nb/app/msg/service/LocalAppConsultService.java
  52. 20 0
      nb-service/app-msg/src/main/java/com/nb/app/msg/service/LocalAppMsgService.java
  53. 2 12
      nb-service/web-service/src/main/java/com/nb/web/service/bus/controller/BusDeviceHistoryController.java
  54. 60 15
      nb-service/web-service/src/main/java/com/nb/web/service/bus/controller/BusInfusionHistoryController.java
  55. 9 4
      nb-service/web-service/src/main/java/com/nb/web/service/bus/controller/BusPatientController.java
  56. 1 0
      nb-service/web-service/src/main/java/com/nb/web/service/bus/entity/BusPatientEntity.java
  57. 8 6
      nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/HisScriptSession.java
  58. 25 0
      nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusDeviceHistoryService.java
  59. 4 6
      nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusPatientService.java
  60. 52 0
      nb-service/web-service/src/main/java/com/nb/web/service/bus/service/dto/TestBusInfusionHistory.java
  61. 5 0
      nb-service/web-service/src/main/java/com/nb/web/service/bus/utils/WsPublishUtils.java
  62. 2 1
      nb-service/web-service/src/main/java/com/nb/web/service/system/auth/WebAuthGranter.java
  63. 2 0
      nb-service/web-service/src/main/java/com/nb/web/service/system/service/ISysRoleService.java
  64. 21 11
      nb-service/web-service/src/main/java/com/nb/web/service/system/service/impl/SysRoleServiceImpl.java
  65. 12 0
      pom.xml

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

@@ -7,19 +7,23 @@ app:
   # 获取ip地址开关
   addressEnabled: true
   # 上传类型,minio;aliyun
-  uploadType: minio
+  uploadType: aliyun
   # 上传目录
   uploadDir: D:/${app.name}-files
   # 缓存前缀
   cachePrefix: ${app.name}:dev
 # MinIO相关配置
 minio:
-  endpoint: 192.168.100.32
+  uploadEndpoint: 192.168.100.32
+  uploadPort: 7001
+  uploadSecure: false
+  downEndpoint: 192.168.100.32
+  downPort: 7002
+  downSecure: false
   accessKey: nbnetpump
   secretKey: tuoren123
   bucketName: ${app.name}-${profiles.active}-bucket
-  port: 7001
-  secure: false
+
 
 # 数据源配置
 spring:
@@ -134,6 +138,13 @@ aliyun:
   product:
     productKey: he1fACg7ySx
 
+oss:
+  aliyun:
+    accessKey: "LTAI4G7FA9ytMc76oNkJ45YJ"
+    accessSecret: "R7hOvMfiHb0PYroDqUDXAYgB9htQss"
+    endpoint: "oss-cn-hangzhou.aliyuncs.com"
+    bucketName: "tuoren-nb-dev"
+
 notify:
   wechat:
     url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=c3e093fe-5125-47d5-a171-0f4be2f61a78

+ 15 - 4
nb-admin/src/main/resources/application-prod.yml

@@ -7,7 +7,7 @@ app:
   # 获取ip地址开关
   addressEnabled: false
   # 上传类型,minio;aliyun
-  uploadType: minio
+  uploadType: aliyun
   # 上传目录
   uploadDir: D:/${app.name}-files
   # 缓存前缀
@@ -16,9 +16,14 @@ app:
 # MinIO相关配置
 # MinIO相关配置
 minio:
-  endpoint: pca.tuoren.com
-  accessKey: tuoren
-  secretKey: Tuoren@123
+  upload:
+    endpoint: pca.tuoren.com
+    accessKey: tuoren
+    secretKey: Tuoren@123
+  down:
+    endpoint: pca.tuoren.com
+    accessKey: tuoren
+    secretKey: Tuoren@123
   bucketName: ${app.name}-${profiles.active}-bucket
   port: 5001
   secure: true
@@ -125,6 +130,12 @@ aliyun:
   product:
     productKey: he1f6YdSWHW
 
+oss:
+  aliyun:
+    accessKey: "LTAI4G7FA9ytMc76oNkJ45YJ"
+    accessSecret: "R7hOvMfiHb0PYroDqUDXAYgB9htQss"
+    endpoint: "oss-cn-hangzhou.aliyuncs.com"
+    bucketName: "tuoren-nb-prod"
 notify:
   wechat:
     url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=ebeb023b-6f39-4f08-bd5a-67c5f3653107

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

@@ -3,6 +3,7 @@ spring:
     multipart:
       max-file-size: 1024MB
       max-request-size: 1024MB
+      enabled: true
   application:
     name: nb
   profiles:
@@ -19,11 +20,12 @@ server:
     accesslog:
       enabled: true
     max-http-post-size: -1
-    buffer-size: 512
+    buffer-size: 1024
     direct-buffers: true
     threads:
       io: 16
       worker: 256
+    no-request-timeout: 3m
 tio:
   websocket:
     server:

+ 21 - 1
nb-admin/src/test/java/com/nb/admin/BusClinicTest.java

@@ -5,7 +5,11 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.json.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.nb.app.assistant.sms.SmsHelper;
 import com.nb.core.result.R;
+import com.nb.oss.strategy.FileStorageStrategy;
+import com.nb.oss.strategy.context.FileStorageContext;
+import com.nb.oss.strategy.entity.SysStorage;
 import com.nb.web.api.enums.ClinicManageEnum;
 import com.nb.web.service.bus.controller.BusDeviceHistoryController;
 import com.nb.web.service.bus.controller.vo.ClinicStatsVo;
@@ -29,6 +33,7 @@ import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.test.context.junit4.SpringRunner;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.annotation.Resource;
 import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
@@ -59,6 +64,21 @@ public class BusClinicTest {
     private LocalBusPatientService patientService;
     @Autowired
     private BusDeviceHistoryController historyController;
+
+    @Autowired
+    private SmsHelper smsHelper;
+    @Resource
+    private FileStorageContext fileStorageContext;
+
+//    @Test
+//    public void upload(){
+//        FileStorageStrategy fileStorageStrategy = fileStorageContext.getContext();
+//        SysStorage storage = fileStorageStrategy.upload(file, String.valueOf(System.currentTimeMillis()));
+//    }
+    @Test
+    public void sendSms(){
+        smsHelper.sendVerifyCode("18339543638","456789");
+    }
     @Test
     public void lost(){
         R<BigDecimal> bigDecimalR = historyController.lossRate("1575009329758154754");
@@ -120,7 +140,7 @@ public class BusClinicTest {
     @Test
     public void updateManage(){
         patientService.setManageType("1579644321062793219", ClinicManageEnum.hospital);
-        patientService.setManageType("1579644321062793219", ClinicManageEnum.family);
+//        patientService.setManageType("1579644321062793219", ClinicManageEnum.family);
         while (true){
 
         }

+ 19 - 0
nb-admin/src/test/java/com/nb/admin/NotifyTest.java

@@ -1,6 +1,11 @@
 package com.nb.admin;
 
 import cn.hutool.json.JSONUtil;
+import com.nb.app.doctor.service.LocalAppDoctorUserService;
+import com.nb.im.entity.ImMsgEntity;
+import com.nb.im.enums.SponsorEnum;
+import com.nb.im.event.ImMsgEvent;
+import com.nb.im.service.LocalImMsgService;
 import com.nb.web.service.bus.job.DeviceFlowStatsJob;
 import com.nb.common.config.notice.msg.DiskMsg;
 import com.nb.common.config.notice.wechat.EnterpriseWeChatNotify;
@@ -33,6 +38,20 @@ public class NotifyTest {
     DiskSpaceHealthIndicator spaceHealthIndicator;
 
     @Autowired
+    LocalAppDoctorUserService appDoctorUserService;
+
+    @Autowired
+    LocalImMsgService imMsgService;
+    @Test
+    public void autoApply(){
+        ImMsgEntity byId = imMsgService.getById("1580015983344975874");
+        byId.setSponsor(SponsorEnum.assist);
+        ImMsgEvent imMsgEvent = new ImMsgEvent(this, byId);
+        appDoctorUserService.assistListener(imMsgEvent);
+        while (true){}
+
+    }
+
     @Test
     public void test(){
         flowStatsJob.flowStats();

+ 1 - 0
nb-auth/src/main/java/com/nb/auth/bean/LoginUser.java

@@ -41,6 +41,7 @@ public class LoginUser<T> implements Serializable {
      */
     private String username;
 
+    private String nickName;
     /**
      * 登录时间
      */

+ 2 - 1
nb-common/delay-queue-common/src/main/java/com/nb/common/queue/delay/RedissonDelayMessageManager.java

@@ -63,7 +63,8 @@ public class RedissonDelayMessageManager implements DelayMessageManager {
         log.info("redisson-delay-queue ,add message = 【{}】",JSONUtil.toJsonStr(message));
         long expire = message.getProperties().getExpire();
         if(expire<=0){
-            handler(message);
+            //       不立即处理,给予500ms缓冲,防止数据库锁冲突   handler(message);
+            delayedQueue.offerAsync(message, 500,TimeUnit.MILLISECONDS);
         }else {
             delayedQueue.offerAsync(message, message.getProperties().getExpire(), message.getProperties().getTimeUnit());
         }

+ 1 - 1
nb-im/src/main/java/com/nb/im/delay/ImRoomAutoFinishDelayHandler.java

@@ -70,7 +70,7 @@ public class ImRoomAutoFinishDelayHandler implements DelayMessageHandler {
                     }
                 }
             }else {
-                log.error("聊天室{}成功建立但是缺少successTime,无法24小时自动关闭");
+                log.error("聊天室{}成功建立但是缺少successTime,无法24小时自动关闭",roomId);
             }
         }else {
             log.info("聊天室未处于成功建立状态,处理聊天室超过24小时自动关闭失败,{}", roomId);

+ 8 - 1
nb-im/src/main/java/com/nb/im/entity/ImRoomEntity.java

@@ -1,5 +1,6 @@
 package com.nb.im.entity;
 
+import cn.hutool.core.collection.CollectionUtil;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.nb.app.msg.bean.MsgBean;
 import com.nb.core.entity.GenericEntity;
@@ -16,6 +17,7 @@ import lombok.ToString;
 
 import javax.validation.constraints.NotNull;
 import java.util.Date;
+import java.util.Map;
 
 /**
  * @author lifang
@@ -107,7 +109,12 @@ public class ImRoomEntity extends GenericEntity<String> {
         //等待医生确认
         result.setStatus(ImStatusEnum.WAITING);
         //默认院内管理
-        result.setManageType(ClinicManageEnum.hospital);
+        Map<String, Object> properties = source.getProperties();
+        ClinicManageEnum manageType=ClinicManageEnum.hospital;
+        if(CollectionUtil.isNotEmpty(properties)){
+            manageType = (ClinicManageEnum) properties.getOrDefault("manageType", ClinicManageEnum.hospital);
+        }
+        result.setManageType(manageType);
         return result;
     }
 }

+ 26 - 0
nb-im/src/main/java/com/nb/im/event/ImFinishedEvent.java

@@ -0,0 +1,26 @@
+package com.nb.im.event;
+
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName ImFinishedEvent.java
+ * @Description 聊天室結束
+ * @createTime 2022年10月13日 10:56:00
+ */
+@Getter
+public class ImFinishedEvent extends ApplicationEvent {
+    private String assistId;
+    private String doctorId;
+    private String patientId;
+
+    public ImFinishedEvent(Object source, String assistId, String doctorId, String patientId) {
+        super(source);
+        this.assistId = assistId;
+        this.doctorId = doctorId;
+        this.patientId = patientId;
+    }
+
+}

+ 27 - 4
nb-im/src/main/java/com/nb/im/job/ImRoomJob.java

@@ -1,18 +1,26 @@
 package com.nb.im.job;
 
+import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.date.DateField;
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.nb.im.entity.ImRoomEntity;
 import com.nb.im.enums.ImStatusEnum;
+import com.nb.im.event.ImFinishedEvent;
 import com.nb.im.service.LocalImRoomService;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.scheduling.annotation.Scheduled;
 
+import javax.validation.constraints.NotNull;
 import java.util.Date;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * @author lifang
@@ -40,12 +48,27 @@ public class ImRoomJob {
             Date now = new Date();
             //24小时之前的时间
             DateTime offset = DateUtil.offset( now, DateField.HOUR, -24);
-            imRoomService.update(new UpdateWrapper<ImRoomEntity>()
+            List<ImRoomEntity> rooms = imRoomService.list(new QueryWrapper<ImRoomEntity>()
                     .lambda()
                     .lt(ImRoomEntity::getSuccessTime, offset)
-                    .eq(ImRoomEntity::getStatus, ImStatusEnum.SUCCESS)
-                    .set(ImRoomEntity::getStatus, ImStatusEnum.AUTO_LIST)
-                    .set(ImRoomEntity::getUpdateTime,now));
+                    .eq(ImRoomEntity::getStatus, ImStatusEnum.SUCCESS));
+            if(CollectionUtil.isNotEmpty(rooms)){
+                Set<String> roomIds = rooms.stream().map(ImRoomEntity::getId).collect(Collectors.toSet());
+                boolean result = imRoomService.update(new UpdateWrapper<ImRoomEntity>()
+                        .lambda()
+                        .in(ImRoomEntity::getId, roomIds)
+                        .set(ImRoomEntity::getStatus, ImStatusEnum.AUTO_LIST)
+                        .set(ImRoomEntity::getUpdateTime, now)
+                );
+                if(result){
+                    rooms
+                            .stream()
+                            .map(room->
+                                    new ImFinishedEvent(this,room.getAssistId(),room.getDoctorId(),room.getPatientId())
+                            ).forEach(SpringUtil::publishEvent);
+                }
+            }
+
         }catch (Exception e){
             log.error("24小时自动结束聊天室失败,",e);
         }

+ 34 - 5
nb-im/src/main/java/com/nb/im/listener/ImPatientInfoListener.java

@@ -1,9 +1,14 @@
 package com.nb.im.listener;
 
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.nb.app.assistant.api.event.AssistLiftEvent;
 import com.nb.im.entity.ImRoomEntity;
 import com.nb.im.enums.ImStatusEnum;
+import com.nb.im.event.ImFinishedEvent;
+import com.nb.app.assistant.api.event.ImNoneEvent;
 import com.nb.im.service.LocalImRoomService;
 import com.nb.web.api.event.PatientInfoEvent;
 import lombok.AllArgsConstructor;
@@ -11,6 +16,8 @@ import org.springframework.context.annotation.Configuration;
 import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.Async;
 
+import java.util.List;
+
 /**
  * @author lifang
  * @version 1.0.0
@@ -40,11 +47,33 @@ public class ImPatientInfoListener {
     @EventListener
     @Async
     public void assistLift(AssistLiftEvent event){
-        roomService.update(new UpdateWrapper<ImRoomEntity>()
+        List<ImRoomEntity> rooms = roomService.list(new QueryWrapper<ImRoomEntity>()
+                .lambda()
+                .eq(ImRoomEntity::getAssistId, event.getAssistId())
+                .eq(ImRoomEntity::getPatientId, event.getPatientId())
+                .in(ImRoomEntity::getStatus, ImStatusEnum.SUCCESS, ImStatusEnum.WAITING)
+        );
+        if (CollectionUtil.isNotEmpty(rooms)) {
+            rooms.stream().map(ImRoomEntity::getId)
+                    .forEach(roomId->roomService.finished(roomId,true));
+        }
+    }
+
+
+
+    @EventListener
+    @Async
+    public void imFinished(ImFinishedEvent event){
+        long count = roomService.count(new QueryWrapper<ImRoomEntity>()
                 .lambda()
-                .eq(ImRoomEntity::getAssistId,event.getAssistId())
-                .eq(ImRoomEntity::getPatientId,event.getPatientId())
-                .in(ImRoomEntity::getStatus,ImStatusEnum.SUCCESS,ImStatusEnum.WAITING)
-                .set(ImRoomEntity::getStatus,ImStatusEnum.ASSIST_LIFT));
+                .eq(ImRoomEntity::getAssistId, event.getAssistId())
+                .eq(ImRoomEntity::getDoctorId, event.getDoctorId())
+                .eq(ImRoomEntity::getPatientId, event.getPatientId())
+                .eq(ImRoomEntity::getStatus, ImStatusEnum.SUCCESS));
+        if(count==0){
+            SpringUtil.publishEvent(new ImNoneEvent(this,event.getAssistId(),event.getDoctorId(),event.getPatientId()));
+        }
+
     }
+
 }

+ 32 - 21
nb-im/src/main/java/com/nb/im/listener/MonitorStatusListener.java

@@ -1,5 +1,6 @@
 package com.nb.im.listener;
 
+import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.nb.im.entity.ImRoomEntity;
 import com.nb.im.enums.ImStatusEnum;
@@ -7,9 +8,12 @@ import com.nb.im.service.LocalImRoomService;
 import com.nb.web.api.event.ClinicFinishedEvent;
 import com.nb.web.api.event.ClinicRestartEvent;
 import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.event.EventListener;
 import org.springframework.scheduling.annotation.Async;
+import org.springframework.transaction.annotation.Isolation;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDateTime;
 import java.time.ZoneId;
@@ -25,37 +29,44 @@ import java.util.Date;
  */
 @Configuration
 @AllArgsConstructor
+@Slf4j
 public class MonitorStatusListener {
     private final LocalImRoomService roomService;
     @EventListener
     @Async
+    @Transactional(isolation = Isolation.SERIALIZABLE)
     public void monitorFinished(ClinicFinishedEvent event){
-        String patientId = event.getPatientId();
+        synchronized (event.getPatientId()){
+            log.info("临床结束事件监听,{}", JSONUtil.toJsonStr(event));
+            String patientId = event.getPatientId();
 
-        roomService.update(new UpdateWrapper<ImRoomEntity>()
-                .lambda()
-                .eq(ImRoomEntity::getPatientId,patientId)
-                .in(ImRoomEntity::getStatus, ImStatusEnum.WAITING,ImStatusEnum.SUCCESS)
-                .set(ImRoomEntity::getMonitorFinishedTime,new Date())
-                .set(ImRoomEntity::getMonitorFinished,true)
-        );
+            roomService.update(new UpdateWrapper<ImRoomEntity>()
+                    .lambda()
+                    .eq(ImRoomEntity::getPatientId,patientId)
+                    .in(ImRoomEntity::getStatus, ImStatusEnum.WAITING,ImStatusEnum.SUCCESS)
+                    .set(ImRoomEntity::getMonitorFinishedTime,new Date())
+                    .set(ImRoomEntity::getMonitorFinished,true));
+        }
     }
 
     @EventListener
     @Async
     public void monitorReset(ClinicRestartEvent event){
-        String patientId = event.getPatientId();
-        //两小时前的时间
-        LocalDateTime dateTime = LocalDateTime.now().plus(-2, ChronoUnit.HOURS);
-        Date date = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
-        //临床结束未满两小时的进行处理
-        roomService.update(new UpdateWrapper<ImRoomEntity>()
-                .lambda()
-                .eq(ImRoomEntity::getPatientId,patientId)
-                .in(ImRoomEntity::getStatus, ImStatusEnum.WAITING,ImStatusEnum.SUCCESS)
-                .eq(ImRoomEntity::getMonitorFinished,true)
-                .gt(ImRoomEntity::getMonitorFinishedTime,date)
-                .set(ImRoomEntity::getMonitorFinished,false)
-        );
+        synchronized (event.getPatientId()){
+            log.info("临床重启事件监听,{}", JSONUtil.toJsonStr(event));
+            String patientId = event.getPatientId();
+            //两小时前的时间
+            LocalDateTime dateTime = LocalDateTime.now().plus(-2, ChronoUnit.HOURS);
+            Date date = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
+            //临床结束未满两小时的进行处理
+            roomService.update(new UpdateWrapper<ImRoomEntity>()
+                    .lambda()
+                    .eq(ImRoomEntity::getPatientId,patientId)
+                    .in(ImRoomEntity::getStatus, ImStatusEnum.WAITING,ImStatusEnum.SUCCESS)
+                    .eq(ImRoomEntity::getMonitorFinished,true)
+                    .gt(ImRoomEntity::getMonitorFinishedTime,date)
+                    .set(ImRoomEntity::getMonitorFinished,false)
+            );
+        }
     }
 }

+ 30 - 4
nb-im/src/main/java/com/nb/im/service/LocalImRoomService.java

@@ -2,9 +2,11 @@ package com.nb.im.service;
 
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.IdWorker;
 import com.nb.app.assistant.api.entity.AssistantUserBindEntity;
+import com.nb.app.assistant.api.event.ImSuccessEvent;
 import com.nb.app.assistant.api.event.UpdateBindPatientEvent;
 import com.nb.app.assistant.api.feign.result.UpdateBindPatientParam;
 import com.nb.app.msg.enums.MsgEnum;
@@ -17,6 +19,7 @@ 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.ImFinishedEvent;
 import com.nb.im.mapper.ImRoomMapper;
 import com.nb.im.room.ImRoomOperator;
 import com.nb.im.room.ImRoomOperatorManager;
@@ -30,6 +33,7 @@ import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.swing.*;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.Map;
@@ -101,7 +105,12 @@ public class LocalImRoomService extends BaseService<ImRoomMapper, ImRoomEntity,S
 
     @Override
     public void validateBeforeUpdate(ImRoomEntity entity) {
-
+        if(ImStatusEnum.SUCCESS.equals(entity.getStatus())&&entity.getSuccessTime()==null){
+            ImRoomEntity room = this.getById(entity.getId());
+            if(room.getSuccessTime()==null){
+                entity.setSuccessTime(new Date());
+            }
+        }
     }
 
     @Override
@@ -113,13 +122,15 @@ public class LocalImRoomService extends BaseService<ImRoomMapper, ImRoomEntity,S
     public void postUpdate(ImRoomEntity entity) {
         if(ImStatusEnum.SUCCESS.equals(entity.getStatus())){
             autoFinishRoom(entity.getId());
+            SpringUtil.publishEvent(new ImSuccessEvent(this,entity.getAssistId(),entity.getDoctorId(),entity.getPatientId()));
         }
     }
 
     @Override
     public void postSave(ImRoomEntity entity) {
         if(ImStatusEnum.SUCCESS.equals(entity.getStatus())){
-            autoFinishRoom(entity.getId());        }
+            autoFinishRoom(entity.getId());
+            SpringUtil.publishEvent(new ImSuccessEvent(this,entity.getAssistId(),entity.getDoctorId(),entity.getPatientId()));}
     }
 
     /**
@@ -160,12 +171,17 @@ public class LocalImRoomService extends BaseService<ImRoomMapper, ImRoomEntity,S
      */
     @Transactional(rollbackFor = Exception.class)
     public void successChatRoom(String chatRoomId,String doctorId) {
+        ImRoomEntity room = this.getById(chatRoomId);
+        if(room==null){
+            return;
+        }
         if (this.update(new UpdateWrapper<ImRoomEntity>()
                 .lambda()
                 .eq(ImRoomEntity::getId,chatRoomId)
                 .set(ImRoomEntity::getSuccessTime,new Date())
                 .set(ImRoomEntity::getStatus, ImStatusEnum.SUCCESS))) {
             autoFinishRoom(chatRoomId);
+            SpringUtil.publishEvent(new ImSuccessEvent(this,room.getAssistId(),room.getDoctorId(),room.getPatientId()));
         }
     }
 
@@ -182,13 +198,17 @@ public class LocalImRoomService extends BaseService<ImRoomMapper, ImRoomEntity,S
     }
 
     @Transactional(rollbackFor = Exception.class)
-    public Boolean finished(String roomId) {
+    public Boolean finished(String roomId,boolean assistLift){
+        ImRoomEntity room = this.getById(roomId);
+        if(room==null){
+            return true;
+        }
         boolean result = this.update(new UpdateWrapper<ImRoomEntity>()
                 .lambda()
                 .eq(ImRoomEntity::getId, roomId)
                 .set(ImRoomEntity::getStatus, ImStatusEnum.DOCTOR_LIFT));
 
-        if(result){
+        if(result&&!assistLift){
             ImRoomOperator roomOperator = roomOperatorManager.getRoomOperator(roomId);
             imUtils.send(roomId,
                     PubMsgInfo.builder()
@@ -201,10 +221,16 @@ public class LocalImRoomService extends BaseService<ImRoomMapper, ImRoomEntity,S
                             .operationType(MsgEnum.FINISHED)
                             .build());
             roomOperator.close();
+            SpringUtil.publishEvent( new ImFinishedEvent(this,room.getAssistId(),room.getDoctorId(),room.getPatientId()));
         }
         return result;
     }
 
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean finished(String roomId) {
+        return finished(roomId,false);
+    }
+
 
     private void autoFinishRoom(String roomId){
         delayMessageManager.add(new DelayMessage(Value.simple(roomId),AUTO_FINISH_DELAY, DelayMessageProperties.of(TimeUnit.MINUTES,1)));

+ 5 - 1
nb-oss/pom.xml

@@ -12,7 +12,11 @@
     <artifactId>nb-oss</artifactId>
 
     <dependencies>
-
+        <dependency>
+            <groupId>com.aliyun.oss</groupId>
+            <artifactId>aliyun-sdk-oss</artifactId>
+            <version>3.15.0</version>
+        </dependency>
         <dependency>
             <groupId>io.minio</groupId>
             <artifactId>minio</artifactId>

+ 26 - 0
nb-oss/src/main/java/com/nb/oss/strategy/AliyunOssProperties.java

@@ -0,0 +1,26 @@
+package com.nb.oss.strategy;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 读取Minio相关配置
+ *
+ * @author Kevin
+ */
+@ConfigurationProperties(prefix = "oss.aliyun")
+@Data
+public class AliyunOssProperties{
+
+
+    private String accessKey;
+
+    private String accessSecret;
+
+    private String endpoint;
+
+
+    private String bucketName;
+
+}

+ 63 - 0
nb-oss/src/main/java/com/nb/oss/strategy/AliyunOssUtil.java

@@ -0,0 +1,63 @@
+package com.nb.oss.strategy;
+
+import com.aliyun.oss.OSS;
+import com.aliyun.oss.OSSClient;
+import com.aliyun.oss.OSSClientBuilder;
+import com.aliyun.oss.model.CannedAccessControlList;
+import com.aliyun.oss.model.PutObjectRequest;
+import com.aliyun.oss.model.PutObjectResult;
+import com.nb.core.exception.CustomException;
+import io.minio.BucketExistsArgs;
+import io.minio.MakeBucketArgs;
+import io.minio.MinioClient;
+import io.minio.PutObjectArgs;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * Minio工具类
+ *
+ * @author Kevin
+ */
+@Component
+@Slf4j
+@EnableConfigurationProperties(AliyunOssProperties.class)
+public class AliyunOssUtil {
+    @Autowired
+    private AliyunOssProperties ossProperties;
+    @Autowired
+    @Lazy
+    private OSS ossClient;
+
+    @Bean
+    public OSS ossClient(){
+        return  new OSSClientBuilder().build(ossProperties.getEndpoint(), ossProperties.getAccessKey(), ossProperties.getAccessSecret());
+    }
+
+    public String uploadObject(InputStream stream, String filepath) {
+        try {
+            boolean exists = ossClient.doesBucketExist(ossProperties.getBucketName());
+            if (!exists) {
+                ossClient.createBucket(ossProperties.getBucketName());
+                ossClient.setBucketAcl(ossProperties.getBucketName(), CannedAccessControlList.PublicReadWrite);
+            }
+            PutObjectRequest putObjectRequest = new PutObjectRequest(ossProperties.getBucketName(), filepath.startsWith("/")?filepath.substring(1):filepath,stream);
+            ossClient.putObject(putObjectRequest);
+        } catch (Exception e) {
+            log.error("上传文件失败,{}", e.getMessage());
+            throw new CustomException("上传文件失败," + e.getMessage());
+        }
+        return  filepath;
+    }
+
+}

+ 28 - 6
nb-oss/src/main/java/com/nb/oss/strategy/MinioConfig.java

@@ -13,14 +13,36 @@ import org.springframework.stereotype.Component;
 @ConfigurationProperties(prefix = "minio")
 @Data
 public class MinioConfig {
+    /***'
+     * 上传路径
+     */
+    private String uploadEndpoint;
+
+    /***'
+     * 上传路径
+     */
+    private int uploadPort;
+    /***'
+     * 上传路径
+     */
+    private boolean uploadSecure;
+
+
+    /***'
+     * 下载路径
+     */
+    private String downEndpoint;
+
+    /***'
+     * 下载路径
+     */
+    private int downPort;
+    /***'
+     * 下载路径
+     */
+    private boolean downSecure;
 
 
-    private String endpoint;
-
-    private int port;
-
-    private boolean secure;
-
     private String accessKey;
 
     private String secretKey;

+ 1 - 1
nb-oss/src/main/java/com/nb/oss/strategy/MinioUtil.java

@@ -28,7 +28,7 @@ public class MinioUtil {
     @Bean
     private MinioClient minioClient() {
         MinioClient minioClient = MinioClient.builder()
-                .endpoint(minioConfig.getEndpoint(),minioConfig.getPort(),minioConfig.isSecure())
+                .endpoint(minioConfig.getUploadEndpoint(),minioConfig.getUploadPort(),minioConfig.isUploadSecure())
                 .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
                 .build();
         return minioClient;

+ 1 - 1
nb-oss/src/main/java/com/nb/oss/strategy/doc/OssDocConfig.java

@@ -18,7 +18,7 @@ import springfox.documentation.spring.web.plugins.Docket;
 @Component
 public class OssDocConfig {
     @Bean
-    @Profile("dev")
+//    @Profile("dev")
     public Docket ossDoc(){
         return new Docket(DocumentationType.SWAGGER_2)
                 .select()

+ 2 - 2
nb-oss/src/main/java/com/nb/oss/strategy/impl/AbstractFileStorage.java

@@ -18,9 +18,9 @@ import java.io.IOException;
 public abstract class AbstractFileStorage implements FileStorageStrategy {
 
     /**
-     * 默认大小 50M
+     * 默认大小 200M
      */
-    private static long DEFAULT_FILE_MAX_SIZE = 50 * 1024 * 1024;
+    private static long DEFAULT_FILE_MAX_SIZE = 200 * 1024 * 1024;
 
     @Override
     public void checkFile(MultipartFile file) {

+ 38 - 2
nb-oss/src/main/java/com/nb/oss/strategy/impl/AliyunStorageStrategy.java

@@ -1,18 +1,36 @@
 package com.nb.oss.strategy.impl;
 
+import cn.hutool.core.text.CharSequenceUtil;
 import com.nb.core.enums.FileStorageStrategyEnum;
+import com.nb.oss.strategy.AliyunOssProperties;
+import com.nb.oss.strategy.AliyunOssUtil;
+import com.nb.oss.strategy.FileUtil;
 import com.nb.oss.strategy.entity.SysStorage;
+import com.nb.oss.strategy.service.LocalSysStorageService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.annotation.Resource;
+import java.io.IOException;
+
 /**
  * Aliyun文件存储策略
  *
  * @author Kevin
  */
 @Service
+@Slf4j
 public class AliyunStorageStrategy extends AbstractFileStorage {
+    @Autowired
+    private AliyunOssUtil ossUtil;
+    @Autowired
+    AliyunOssProperties ossProperties;
 
+    @Resource
+    LocalSysStorageService sysStorageService;
     @Override
     public String getStrategyName() {
         return FileStorageStrategyEnum.ALIYUN.name();
@@ -20,8 +38,26 @@ public class AliyunStorageStrategy extends AbstractFileStorage {
 
 
     @Override
-    public SysStorage upload(MultipartFile file, String bizPath) {
-        return null;
+    public SysStorage upload(MultipartFile file, String bizPath) throws IOException {
+        this.checkFile(file);
+        String suffix = FileUtil.getExtensionName(file.getOriginalFilename());
+        String name =file.getName();
+        String type = FileUtil.getFileType(suffix);
+        String filepath = this.getFilepath(file, bizPath);
+        String url = ossUtil.uploadObject(file.getInputStream(), filepath);
+        String urlPrefix= "https://"+ ossProperties.getBucketName()+"."+ossProperties.getEndpoint();
+        name = CharSequenceUtil.isBlank(name) ? FileUtil.getFileNameNoEx(file.getOriginalFilename()) : name;
+
+        SysStorage localStorage = new SysStorage(file.getName(), name, suffix,urlPrefix,url, type,
+                file.getSize());
+
+        sysStorageService.save(localStorage);
+
+        log.info("上传文件原始名称:{}", file.getOriginalFilename());
+        log.info("上传文件路径:{}", filepath);
+
+        log.info("上传文件URL:{}", url);
+        return localStorage;
     }
 
 }

+ 1 - 1
nb-oss/src/main/java/com/nb/oss/strategy/impl/MinioStorageStrategy.java

@@ -46,7 +46,7 @@ public class MinioStorageStrategy extends AbstractFileStorage {
         String type = FileUtil.getFileType(suffix);
         String filepath = this.getFilepath(file, bizPath);
         String url = minioUtils.uploadObject(file.getInputStream(), filepath);
-        String urlPrefix= (minioConfig.isSecure()?"https://":"http://")+minioConfig.getEndpoint()+":"+minioConfig.getPort() ;
+        String urlPrefix= (minioConfig.isDownSecure()?"https://":"http://")+minioConfig.getDownEndpoint()+":"+minioConfig.getDownPort() ;
         name = CharSequenceUtil.isBlank(name) ? FileUtil.getFileNameNoEx(file.getOriginalFilename()) : name;
 
         SysStorage localStorage = new SysStorage(file.getName(), name, suffix,urlPrefix,url, type,

+ 3 - 0
nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/entity/AssistantUserBindEntity.java

@@ -68,6 +68,9 @@ public class AssistantUserBindEntity extends GenericEntity<String> {
     @ApiModelProperty(value = "是否为默认看护")
     private Boolean default_;
 
+    @ApiModelProperty(value = "是否存在聊天室")
+    private Boolean existIm;
+
     @ApiModelProperty(value = "申请类型",required = true,example = " 0(手动申请) 1(邀请码申请)")
     private ApplyEnum applyType;
 

+ 23 - 2
nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/event/AssistLiftEvent.java

@@ -3,21 +3,42 @@ package com.nb.app.assistant.api.event;
 import lombok.Getter;
 import org.springframework.context.ApplicationEvent;
 
+import java.time.Clock;
+
 /**
  * @author lifang
  * @version 1.0.0
  * @ClassName AssistLiftEvent.java
- * @Description 解除看护绑定事件
+ * @Description 医生解除看护绑定事件
  * @createTime 2022年10月11日 15:54:00
  */
 @Getter
 public class AssistLiftEvent extends ApplicationEvent {
     private String assistId;
+    private String assistNickname;
     private String patientId;
+    private String patientCode;
+    private String patientName;
+    private String doctorId;
+    private String doctorNickname;
+    /****
+     * 是否由看护人员主动断开连接
+     */
+    private boolean assistLift;
 
-    public AssistLiftEvent(Object source, String assistId, String patientId) {
+    public AssistLiftEvent(Object source, String assistId, String assistNickname,
+                           String patientId, String patientName,String patientCode,
+                           String doctorId, String doctorNickname,
+                           boolean assistLift) {
         super(source);
         this.assistId = assistId;
+        this.assistNickname = assistNickname;
         this.patientId = patientId;
+        this.patientCode=patientCode;
+        this.patientName = patientName;
+        this.doctorId=doctorId;
+        this.doctorNickname=doctorNickname;
+        this.assistLift=assistLift;
     }
+
 }

+ 25 - 0
nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/event/ImNoneEvent.java

@@ -0,0 +1,25 @@
+package com.nb.app.assistant.api.event;
+
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName ImNoneEvent.java
+ * @Description 病人、看护人、医生三者之间的聊天室全部结束
+ * @createTime 2022年10月13日 11:00:00
+ */
+@Getter
+public class ImNoneEvent extends ApplicationEvent {
+    private String assistId;
+    private String doctorId;
+    private String patientId;
+
+    public ImNoneEvent(Object source, String assistId, String doctorId, String patientId) {
+        super(source);
+        this.assistId = assistId;
+        this.doctorId = doctorId;
+        this.patientId = patientId;
+    }
+}

+ 25 - 0
nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/event/ImSuccessEvent.java

@@ -0,0 +1,25 @@
+package com.nb.app.assistant.api.event;
+
+import lombok.Getter;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName ImNoneEvent.java
+ * @Description 病人、看护人、医生三者之间的聊天室成功建立
+ * @createTime 2022年10月13日 11:00:00
+ */
+@Getter
+public class ImSuccessEvent extends ApplicationEvent {
+    private String assistId;
+    private String doctorId;
+    private String patientId;
+
+    public ImSuccessEvent(Object source, String assistId, String doctorId, String patientId) {
+        super(source);
+        this.assistId = assistId;
+        this.doctorId = doctorId;
+        this.patientId = patientId;
+    }
+}

+ 12 - 1
nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/feign/IAssistantUserBindClient.java

@@ -3,6 +3,7 @@ package com.nb.app.assistant.api.feign;
 import com.nb.app.assistant.api.bean.HandleBindResult;
 import com.nb.app.assistant.api.entity.AssistantUserBindEntity;
 import com.nb.app.assistant.api.feign.result.ContactQuery;
+import com.nb.app.assistant.api.feign.result.PatientBindAssistantResult;
 import com.nb.app.assistant.api.feign.result.UpdateBindPatientParam;
 
 import java.util.*;
@@ -41,10 +42,11 @@ public interface IAssistantUserBindClient {
      * @param bindId
      * @param doctorId 医生解除绑定
      * @param  assistId 患者解除绑定
+     * @param  assistLift 是否由看护人员解除绑定
      * assistId与doctorId不可同时为空或同时不为空
      * @return boolean
      */
-    boolean liftBind(String bindId, String doctorId,String assistId);
+    boolean liftBind(String bindId, String doctorId,String assistId,boolean assistLift);
 
     /**
      * 描述: 判断该医院的住院号是否已被看护
@@ -67,4 +69,13 @@ public interface IAssistantUserBindClient {
     boolean haveBindByPatientId(String patientId, String tenantId);
 
     boolean updateBindPatient(UpdateBindPatientParam patientParam);
+
+    /**
+     * 描述: 病人看护人列表
+     * @author lifang
+     * @date 2022/10/13 10:27
+     * @param patientId
+     * @return List<PatientBindAssistantResult>
+     */
+    List<PatientBindAssistantResult> listAssist(String patientId);
 }

+ 42 - 0
nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/feign/result/PatientBindAssistantResult.java

@@ -0,0 +1,42 @@
+package com.nb.app.assistant.api.feign.result;
+
+import com.nb.core.enums.SexEnum;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AssistantUserResult.java
+ * @Description TODO
+ * @createTime 2022年08月13日 09:16:00
+ */
+@Data
+@ApiModel("病人看护人信息")
+public class PatientBindAssistantResult implements Serializable {
+    private static final long serialVersionUID=1L;
+    @ApiModelProperty("绑定关系id")
+    private String id;
+    @ApiModelProperty("看护人Id")
+    private String assistId;
+    @ApiModelProperty("看护人是否在线")
+    private boolean assistOnline;
+    @ApiModelProperty("病人id")
+    private String patientId;
+    @ApiModelProperty("看护人昵称")
+    private String nickname;
+    @ApiModelProperty("看护人头像地址")
+    private String avatar;
+    @ApiModelProperty("看护人手机号")
+    private String phone;
+    @ApiModelProperty("看护人性别")
+    private SexEnum sex;
+    @ApiModelProperty("创建时间")
+    private Date creatTime;
+    @ApiModelProperty("是否存在聊天室")
+    private boolean existImRoom;
+}

+ 3 - 0
nb-service-api/app-msg-api/src/main/java/com/nb/app/msg/bean/MsgBean.java

@@ -7,6 +7,7 @@ import lombok.Builder;
 import lombok.Getter;
 
 import java.io.Serializable;
+import java.util.Map;
 
 /**
  * @author lifang
@@ -63,4 +64,6 @@ public class MsgBean implements Serializable {
     @ApiModelProperty("扩展字段,例:存放咨询问题id")
     private String extend;
 
+    private Map<String,Object> properties;
+
 }

+ 3 - 1
nb-service-api/app-msg-api/src/main/java/com/nb/app/msg/enums/MsgEnum.java

@@ -29,7 +29,9 @@ public enum  MsgEnum  implements IEnum<Integer> {
     ASSIST_EVAL(7,"疼痛评估"),
     NORMAL(8,"正常消息"),
     FINISHED(9,"医生断开聊天"),
-    AUTO_FINISHED(10,"超过24小时自动断开聊天")
+    AUTO_FINISHED(10,"超过24小时自动断开聊天"),
+    ASSIST_LIFT(11,"看护人员解除绑定"),
+    DOCTOR_LIFT(12,"医生解除绑定")
     ;
     @Getter
     @ApiModelProperty("枚举编码")

+ 4 - 0
nb-service/app-assistant/pom.xml

@@ -53,6 +53,10 @@
             <groupId>com.tuoren</groupId>
             <artifactId>delay-queue-common</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>dysmsapi20170525</artifactId>
+        </dependency>
     </dependencies>
 
 

+ 6 - 3
nb-service/app-assistant/src/main/java/com/nb/app/assistant/auth/AssistantPasswordGranter.java

@@ -1,5 +1,6 @@
 package com.nb.app.assistant.auth;
 
+import cn.dev33.satoken.stp.StpLogic;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.nb.app.assistant.api.feign.result.AssistantUserResult;
@@ -62,18 +63,20 @@ public class AssistantPasswordGranter implements IAuthGranter {
         log.info("登录用户:{}", source.getUsername());
 
         // 登录
-        SecurityUtil.getStpLogic(StpTypeEnum.ASSISTANT.getText()).login(user.getId());
+        StpLogic stpLogic = SecurityUtil.getStpLogic(StpTypeEnum.ASSISTANT.getText());
+        stpLogic.login(user.getId());
         LoginUser<String> loginUser = new LoginUser();
-        loginUser.setToken(SecurityUtil.getStpLogic().getTokenValue());
+        loginUser.setToken(stpLogic.getTokenValue());
         loginUser.setUserPlatform(UserPlatformEnum.APP_ASSIST.getCode());
         loginUser.setGrantType(source.getGrantType());
         loginUser.setUsername(source.getUsername());
+        loginUser.setNickName(user.getNickname());
         loginUser.setSys(true);
         loginUser.setId(user.getId());
         loginUser.setLoginType(StpTypeEnum.ASSISTANT.getText());
         fillUserAgentInfo(loginUser);
         // 设置用户信息
-        SecurityUtil.getStpLogic(StpTypeEnum.ASSISTANT.getText()).getTokenSession().set(LOGIN_USER_KEY,loginUser);
+        stpLogic.getTokenSession().set(LOGIN_USER_KEY,loginUser);
         return loginUser;
     }
 }

+ 7 - 4
nb-service/app-assistant/src/main/java/com/nb/app/assistant/auth/AssistantPhoneGranter.java

@@ -1,5 +1,6 @@
 package com.nb.app.assistant.auth;
 
+import cn.dev33.satoken.stp.StpLogic;
 import cn.hutool.core.util.PhoneUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.spring.SpringUtil;
@@ -69,24 +70,26 @@ public class AssistantPhoneGranter implements IAuthGranter {
             user.setId(IdWorker.getIdStr());
             user.setPhone(source.getUsername());
             user.setNickname(source.getUsername());
-            user.setPassword(SecurityUtil.encryptPassword("123456"));
+            user.setPassword(SecurityUtil.encryptPassword("A123456"));
             assistantUserService.save(user);
         }
         log.info("登录用户:{}", source.getUsername());
 
         // 登录
-        SecurityUtil.getStpLogic(StpTypeEnum.ASSISTANT.getText()).login(user.getId());
+        StpLogic stpLogic = SecurityUtil.getStpLogic(StpTypeEnum.ASSISTANT.getText());
+        stpLogic.login(user.getId());
         LoginUser<String> loginUser = new LoginUser();
-        loginUser.setToken(SecurityUtil.getStpLogic().getTokenValue());
+        loginUser.setToken(stpLogic.getTokenValue());
         loginUser.setUserPlatform(UserPlatformEnum.APP_ASSIST.getCode());
         loginUser.setGrantType(source.getGrantType());
         loginUser.setUsername(source.getUsername());
         loginUser.setSys(true);
+        loginUser.setNickName(user.getNickname());
         loginUser.setId(user.getId());
         loginUser.setLoginType(StpTypeEnum.ASSISTANT.getText());
         fillUserAgentInfo(loginUser);
         // 设置用户信息
-        SecurityUtil.getStpLogic(StpTypeEnum.ASSISTANT.getText()).getTokenSession().set(LOGIN_USER_KEY,loginUser);
+        stpLogic.getTokenSession().set(LOGIN_USER_KEY,loginUser);
 
         if(register){
             SpringUtil.publishEvent(new SaveMsgEvent(this,

+ 25 - 2
nb-service/app-assistant/src/main/java/com/nb/app/assistant/controller/CaptchaController.java

@@ -1,11 +1,16 @@
 package com.nb.app.assistant.controller;
 
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.nb.app.assistant.controller.vo.CaptureVo;
 import com.nb.app.assistant.controller.vo.ResetCaptchaVo;
 import com.nb.app.assistant.controller.vo.ResetPswVo;
+import com.nb.app.assistant.entity.AssistantUserEntity;
 import com.nb.app.assistant.enums.CaptchaEnum;
+import com.nb.app.assistant.service.LocalAssistantUserService;
+import com.nb.app.assistant.sms.SmsHelper;
 import com.nb.app.assistant.utils.CaptchaUtil;
 import com.nb.app.assistant.utils.ResetPswUtil;
+import com.nb.core.exception.CustomException;
 import com.nb.core.result.R;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -16,6 +21,8 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.validation.constraints.NotNull;
+
 /**
  * @author lifang
  * @version 1.0.0
@@ -31,14 +38,30 @@ public class CaptchaController {
     private final CaptchaUtil captchaUtil;
 
     private final ResetPswUtil resetPswUtil;
+
+    private final SmsHelper smsHelper;
+
+    private final LocalAssistantUserService assistantUserService;
     @PostMapping("/create")
     @ApiOperation("获取短信验证码")
     public R<String> capture(@RequestBody @Validated CaptureVo vo){
-        return R.success(captchaUtil.getCode(vo.getType(),vo.getPhone()));
+       CaptchaEnum type = vo.getType();
+       if(CaptchaEnum.FORGET_PSW.equals(type)){
+           //判断手机号账户是否存在
+           AssistantUserEntity user = assistantUserService.getOne(new QueryWrapper<AssistantUserEntity>()
+                   .lambda()
+                   .eq(AssistantUserEntity::getPhone, vo.getPhone()));
+           if(user==null){
+               throw new CustomException("该手机号尚未注册");
+           }
+       }
+        String code = captchaUtil.getCode(vo.getType(), vo.getPhone());
+        smsHelper.sendVerifyCode(vo.getPhone(),code);
+        return R.success(code);
     }
 
     @PostMapping("/reset/validate")
-    @ApiOperation(value = "验证码校验",notes = "校验验证码并获取校验token,过期时间24h")
+    @ApiOperation(value = "重置验证码校验",notes = "校验验证码并获取校验token,过期时间24h")
     public R<String> validateCaptcha(@RequestBody@Validated ResetCaptchaVo resource){
         captchaUtil.verifyCode(CaptchaEnum.FORGET_PSW,resource.getPhone(),resource.getCaptcha());
         return R.success(resetPswUtil.createToken(resource.getPhone(),resource.getCaptcha()));

+ 14 - 6
nb-service/app-assistant/src/main/java/com/nb/app/assistant/controller/PatientOperationController.java

@@ -2,6 +2,8 @@ package com.nb.app.assistant.controller;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.druid.sql.visitor.functions.Bin;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.nb.app.assistant.api.enums.ApplyEnum;
 import com.nb.app.assistant.controller.vo.InviteCodePatientVo;
@@ -12,6 +14,8 @@ import com.nb.app.assistant.service.LocalAssistantUserBindService;
 import com.nb.app.assistant.service.dto.AssistPatientResult;
 import com.nb.app.assistant.service.dto.EditAssistBindDto;
 import com.nb.auth.utils.SecurityUtil;
+import com.nb.core.annotation.Log;
+import com.nb.core.enums.UserPlatformEnum;
 import com.nb.core.exception.CustomException;
 import com.nb.core.result.R;
 import com.nb.web.api.feign.IPatientClient;
@@ -113,8 +117,10 @@ public class PatientOperationController {
      * @return R
      */
     @PostMapping("/save")
+    @Log(title = "发起看护人申请",userPlatform = UserPlatformEnum.APP_ASSIST)
     @ApiOperation(value = "发起添加看护人申请")
     public R<Boolean> saveMonitor(@RequestBody MonitorAddVo vo){
+        log.info("发起看护申请{}", JSONUtil.toJsonStr(vo));
         if(ApplyEnum.INVITE_CODE.equals(vo.getApplyType())&& StrUtil.isEmpty(vo.getInviteCode())){
             throw new CustomException("邀请码不能为空");
         }
@@ -127,7 +133,7 @@ public class PatientOperationController {
         if(vo.getManageType()==null){
             vo.setManageType(patientClient.getManageType(vo.getPatientId()));
         }
-        judgePatient(vo.getPatientId());
+        judgePatient(vo.getPatientId(),String.valueOf(SecurityUtil.getId()));
         judgeUser();
         AssistantUserBindEntity resource = BeanUtil.copyProperties(vo, AssistantUserBindEntity.class);
         return R.success(userBindService.save(resource));
@@ -140,7 +146,7 @@ public class PatientOperationController {
         if(inviteCodeResult.getResult()==null){
             throw new CustomException("系统繁忙,请稍后重试");
         }
-        judgePatient(inviteCodeResult.getResult().getPatientId());
+//        judgePatient(inviteCodeResult.getResult().getPatientId());
         judgeUser();
         InviteCodePatientVo result = BeanUtil.copyProperties(inviteCodeResult.getResult(), InviteCodePatientVo.class);
         result.setTenantId(inviteCodeResult.getTenantId());
@@ -166,7 +172,7 @@ public class PatientOperationController {
     @PostMapping("/lift/{bindId}")
     @ApiOperation(value = "与病人解除绑定")
     public R<Boolean> lift(@PathVariable("bindId") String bindId) {
-        return R.success(userBindService.liftBind(bindId,null,String.valueOf(SecurityUtil.getId())));
+        return R.success(userBindService.liftBind(bindId,null,String.valueOf(SecurityUtil.getId()),true));
     }
 
 
@@ -197,17 +203,19 @@ public class PatientOperationController {
      * @author lifang
      * @date 2022/8/12 14:45
      * @param patientId
+     * @param
      * @return void
      */
-    private void judgePatient(String patientId){
+    private void judgePatient(String patientId, String assistId){
         AssistantUserBindEntity userBind = userBindService.getOne(new QueryWrapper<AssistantUserBindEntity>()
                 .lambda()
-                .eq(AssistantUserBindEntity::getStatus, BindEnum.SUCCESS)
+                .eq(AssistantUserBindEntity::getAssistId,assistId)
+                .in(AssistantUserBindEntity::getStatus, BindEnum.WAITING, BindEnum.SUCCESS)
                 .eq(AssistantUserBindEntity::getPatientId, patientId)
         );
 
         if(userBind!=null){
-            throw new CustomException("该病号已被他人监护");
+            throw new CustomException("您已对该病号发起看护请求,请勿重复操作");
         }
     }
 

+ 26 - 0
nb-service/app-assistant/src/main/java/com/nb/app/assistant/listener/AssistPatientInfoListener.java

@@ -3,6 +3,8 @@ package com.nb.app.assistant.listener;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.nb.app.assistant.api.entity.AssistantUserBindEntity;
 import com.nb.app.assistant.api.enums.BindEnum;
+import com.nb.app.assistant.api.event.ImNoneEvent;
+import com.nb.app.assistant.api.event.ImSuccessEvent;
 import com.nb.app.assistant.service.LocalAssistantUserBindService;
 import com.nb.web.api.event.PatientInfoEvent;
 import lombok.AllArgsConstructor;
@@ -34,4 +36,28 @@ public class AssistPatientInfoListener {
                 .set(AssistantUserBindEntity::getPatientSex,patientInfoEvent.getPatientSex())
         );
     }
+
+    @EventListener
+    @Async
+    public void imNone(ImNoneEvent event){
+        userBindService.update(new UpdateWrapper<AssistantUserBindEntity>()
+                .lambda()
+                .eq(AssistantUserBindEntity::getPatientId,event.getPatientId())
+                .eq(AssistantUserBindEntity::getDoctorId,event.getDoctorId())
+                .eq(AssistantUserBindEntity::getAssistId,event.getAssistId())
+                .eq(AssistantUserBindEntity::getStatus, BindEnum.SUCCESS)
+                .set(AssistantUserBindEntity::getExistIm,false));
+    }
+
+    @EventListener
+    @Async
+    public void imNone(ImSuccessEvent event){
+        userBindService.update(new UpdateWrapper<AssistantUserBindEntity>()
+                .lambda()
+                .eq(AssistantUserBindEntity::getPatientId,event.getPatientId())
+                .eq(AssistantUserBindEntity::getDoctorId,event.getDoctorId())
+                .eq(AssistantUserBindEntity::getAssistId,event.getAssistId())
+                .eq(AssistantUserBindEntity::getStatus, BindEnum.SUCCESS)
+                .set(AssistantUserBindEntity::getExistIm,true));
+    }
 }

+ 0 - 1
nb-service/app-assistant/src/main/java/com/nb/app/assistant/listener/ClinicManageInfoListener.java

@@ -5,7 +5,6 @@ import com.nb.app.assistant.api.entity.AssistantUserBindEntity;
 import com.nb.app.assistant.api.enums.BindEnum;
 import com.nb.app.assistant.service.LocalAssistantUserBindService;
 import com.nb.web.api.event.ClinicManageEvent;
-import com.nb.web.api.event.PatientInfoEvent;
 import lombok.AllArgsConstructor;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.event.EventListener;

+ 6 - 0
nb-service/app-assistant/src/main/java/com/nb/app/assistant/mapper/AssistantUserBindMapper.java

@@ -2,7 +2,11 @@ package com.nb.app.assistant.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.nb.app.assistant.api.entity.AssistantUserBindEntity;
+import com.nb.app.assistant.api.feign.result.PatientBindAssistantResult;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 /**
  * @author lifang
@@ -13,4 +17,6 @@ import org.apache.ibatis.annotations.Mapper;
  */
 @Mapper
 public interface AssistantUserBindMapper extends BaseMapper<AssistantUserBindEntity> {
+
+    List<PatientBindAssistantResult> listAssistByPatientId(@Param("patientId") String patientId);
 }

+ 22 - 3
nb-service/app-assistant/src/main/java/com/nb/app/assistant/service/LocalAssistantUserBindService.java

@@ -15,6 +15,7 @@ import com.nb.app.assistant.api.feign.result.AssistantUserResult;
 import com.nb.app.assistant.api.enums.ApplyEnum;
 import com.nb.app.assistant.api.enums.BindEnum;
 import com.nb.app.assistant.api.feign.result.ContactQuery;
+import com.nb.app.assistant.api.feign.result.PatientBindAssistantResult;
 import com.nb.app.assistant.api.feign.result.UpdateBindPatientParam;
 import com.nb.app.assistant.constant.UnEvalConstant;
 import com.nb.app.assistant.delay.UnEvalNotifyConfig;
@@ -32,13 +33,16 @@ import com.nb.common.queue.delay.message.DelayMessage;
 import com.nb.common.queue.delay.message.DelayMessageProperties;
 import com.nb.core.Value;
 import com.nb.core.exception.CustomException;
+import com.nb.web.api.entity.BusClinicEntity;
 import com.nb.web.api.feign.IPatientClient;
+import com.nb.web.api.feign.result.PatientMonitorDetailResult;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -65,7 +69,7 @@ public class LocalAssistantUserBindService extends BaseService<AssistantUserBind
 
     @Override
     public void validateBeforeSave(AssistantUserBindEntity entity) {
-
+        entity.setExistIm(false);
     }
 
     @Override
@@ -80,6 +84,7 @@ public class LocalAssistantUserBindService extends BaseService<AssistantUserBind
 
     @Override
     public boolean save(AssistantUserBindEntity entity) {
+        entity.setExistIm(false);
         String id = String.valueOf(SecurityUtil.getId());
         if(StrUtil.isEmpty(entity.getPatientId())){
             entity.setPatientId(patientClient.lookPatientId(entity.getTenantId(),entity.getPatientCode()));
@@ -193,6 +198,7 @@ public class LocalAssistantUserBindService extends BaseService<AssistantUserBind
 //                    .eq(AssistantUserBindEntity::getStatus, BindEnum.SUCCESS)
 //                    .last("limit 1"));
 //            if(existBind!=null){
+//                return true;
 //                throw new CustomException("该用户已被看护,请勿重复绑定");
 //            }
             //判断当前看护是否可设置为默认看护
@@ -206,9 +212,15 @@ public class LocalAssistantUserBindService extends BaseService<AssistantUserBind
             //当没有默认看护时,设置当前看护为默认看护
             isDefault=defaultAssistant==null;
         }
+        //医生同意后实时更新病人信息
+        PatientMonitorDetailResult patientDetail = patientClient.lookPatientDetail(userBind.getPatientId());
+        BusClinicEntity clinic = Optional.ofNullable(patientDetail.getClinic()).orElse(new BusClinicEntity());
         boolean result = this.update(new UpdateWrapper<AssistantUserBindEntity>()
                 .lambda()
                 .eq(AssistantUserBindEntity::getId, source.getBindId())
+                .set(clinic.getPatientGender()!=null,AssistantUserBindEntity::getPatientSex,clinic.getPatientGender())
+                .set(clinic.getPatientAge()!=null,AssistantUserBindEntity::getPatientAge,clinic.getPatientAge())
+                .set(clinic.getPatientName()!=null,AssistantUserBindEntity::getPatientName,clinic.getPatientName())
                 .set(AssistantUserBindEntity::getDoctorId, source.getDoctorId())
                 .set(AssistantUserBindEntity::getStatus, source.getAgree() ? BindEnum.SUCCESS : BindEnum.REFUSE)
                 .set(source.getAgree(),AssistantUserBindEntity::getDefault_,isDefault)
@@ -256,7 +268,7 @@ public class LocalAssistantUserBindService extends BaseService<AssistantUserBind
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public boolean liftBind(String bindId, String doctorId,String assistId) {
+    public boolean liftBind(String bindId, String doctorId,String assistId,boolean assistLift) {
         if((StrUtil.isNotEmpty(doctorId)&&StrUtil.isNotEmpty(assistId))
                 ||((StrUtil.isEmpty(doctorId)&&StrUtil.isEmpty(assistId)))){
             throw new CustomException("医生id和看护人员id不可同时为空或同时不为空");
@@ -283,7 +295,9 @@ public class LocalAssistantUserBindService extends BaseService<AssistantUserBind
                         .eq(StrUtil.isNotEmpty(assistId),AssistantUserBindEntity::getAssistId, assistId)
                         .set(AssistantUserBindEntity::getStatus,StrUtil.isEmpty(doctorId)? BindEnum.ASSIST_MANUAL_LIFTED: BindEnum.DOCTOR_MANUAL_LIFTED));
         if (result) {
-            SpringUtil.publishEvent(new AssistLiftEvent(this,userBind.getAssistId(),userBind.getPatientId()));
+            SpringUtil.publishEvent(new AssistLiftEvent(this,userBind.getAssistId(),userBind.getAssistNickname(),
+                    userBind.getPatientId(), userBind.getPatientName(),userBind.getPatientCode(),
+                    userBind.getDoctorId(),userBind.getDoctorName(),assistLift));
         }
         return result;
     }
@@ -334,6 +348,11 @@ public class LocalAssistantUserBindService extends BaseService<AssistantUserBind
         return result;
     }
 
+    @Override
+    public List<PatientBindAssistantResult> listAssist(String patientId) {
+        return this.baseMapper.listAssistByPatientId(patientId);
+    }
+
     /**
      * 描述:查询当前用户所看护的所有病人信息
      * @author lifang

+ 31 - 0
nb-service/app-assistant/src/main/java/com/nb/app/assistant/sms/AliSmsClientConfig.java

@@ -0,0 +1,31 @@
+package com.nb.app.assistant.sms;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import com.aliyun.dysmsapi20170525.*;
+import com.aliyun.teaopenapi.models.*;
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliSmsClientConfig.java
+ * @Description TODO
+ * @createTime 2022年10月12日 10:52:00
+ */
+@Configuration
+public class AliSmsClientConfig {
+    @Value("${aliyun.accessKey}")
+    private String accessKeyId;
+    @Value("${aliyun.accessSecret}")
+    private String accessKeySecret;
+    @Bean
+    public Client creatClient() throws Exception {
+        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
+                // 您的 AccessKey ID
+                .setAccessKeyId(accessKeyId)
+                // 您的 AccessKey Secret
+                .setAccessKeySecret(accessKeySecret);
+        config.endpoint = "dysmsapi.aliyuncs.com";
+        return new Client(config);
+    }
+}

+ 48 - 0
nb-service/app-assistant/src/main/java/com/nb/app/assistant/sms/SmsHelper.java

@@ -0,0 +1,48 @@
+package com.nb.app.assistant.sms;
+
+import cn.hutool.core.map.MapUtil;
+import cn.hutool.json.JSONUtil;
+import com.aliyun.dysmsapi20170525.*;
+import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
+import com.aliyun.tea.TeaException;
+import lombok.AllArgsConstructor;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName SmsHelper.java
+ * @Description TODO
+ * @createTime 2022年10月13日 09:28:00
+ */
+@Configuration
+@AllArgsConstructor
+public class SmsHelper {
+
+    private final Client client;
+
+    public void sendVerifyCode(String phone,String code){
+        Map<String, String> map = MapUtil.of("code", code);
+        com.aliyun.dysmsapi20170525.models.SendSmsRequest sendSmsRequest = new com.aliyun.dysmsapi20170525.models.SendSmsRequest()
+                .setPhoneNumbers(phone)
+                .setSignName("驼人医疗")
+                .setTemplateCode("SMS_247815102")
+                .setTemplateParam(JSONUtil.toJsonStr(map));;
+        com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
+        try {
+            // 复制代码运行请自行打印 API 的返回值
+            SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime);
+        } catch (TeaException error) {
+            // 如有需要,请打印 error
+            com.aliyun.teautil.Common.assertAsString(error.message);
+        } catch (Exception _error) {
+            TeaException error = new TeaException(_error.getMessage(), _error);
+            // 如有需要,请打印 error
+            com.aliyun.teautil.Common.assertAsString(error.message);
+        }
+    }
+
+}

+ 33 - 0
nb-service/app-assistant/src/main/resources/mapper/AssistantUserBindMapper.xml

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.nb.app.assistant.mapper.AssistantUserBindMapper">
+
+
+    <resultMap id="assistPatientResult" type="com.nb.app.assistant.api.feign.result.PatientBindAssistantResult">
+        <result property="id" column="id"/>
+        <result property="creatTime" column="create_time"/>
+        <result property="assistId" column="assist_id"/>
+        <result property="nickname" column="nickname"/>
+        <result property="patientId" column="patient_id"/>
+        <result property="avatar" column="avatar"/>
+        <result property="phone" column="phone"/>
+        <result property="phone" column="phone"/>
+        <result property="existImRoom" column="exist_im"/>
+    </resultMap>
+
+    <select id="listAssistByPatientId" resultMap="assistPatientResult" >
+            select ub.id as id,
+            ub.create_time as create_time,
+            IFNULL(ub.exist_im ,0)as exist_im,
+            u.id as assist_id,
+            u.nickname as nickname,
+            ub.patient_id as patient_id,
+            u.avatar as avatar,
+            u.phone as phone
+            from assistant_user_bind as ub INNER JOIN assistant_user as u
+            on ub.assist_id = u.id
+            where ub.status='1' and  ub.patient_id=#{patientId}
+            order by ub.exist_im desc , ub.create_time desc
+    </select>
+
+</mapper>

+ 1 - 0
nb-service/app-doctor/src/main/java/com/nb/app/doctor/auth/AppDoctorAuthGranter.java

@@ -74,6 +74,7 @@ public class AppDoctorAuthGranter implements IAuthGranter {
         loginUser.setTenantId(user.getTenantId());
         loginUser.setId(user.getId());
         loginUser.setLoginType(StpTypeEnum.APP_DOCTOR.getText());
+        loginUser.setNickName(user.getRealName());
         fillUserAgentInfo(loginUser);
         // 设置用户信息
         stpLogic.getTokenSessionByToken(loginUser.getToken(),true).set(LOGIN_USER_KEY,loginUser);

+ 13 - 3
nb-service/app-doctor/src/main/java/com/nb/app/doctor/controller/NoticeMsgController.java

@@ -1,6 +1,11 @@
 package com.nb.app.doctor.controller;
 
+import cn.hutool.core.bean.BeanUtil;
+import com.nb.app.assistant.api.entity.AssistantUserBindEntity;
 import com.nb.app.assistant.api.feign.IAssistantUserBindClient;
+import com.nb.app.assistant.api.feign.IAssistantUserClient;
+import com.nb.app.assistant.api.feign.result.AssistantUserResult;
+import com.nb.app.doctor.service.dto.AssistantUserBindResult;
 import com.nb.core.result.R;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -11,6 +16,8 @@ import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import java.util.Optional;
+
 /**
  * @author lifang
  * @version 1.0.0
@@ -25,10 +32,13 @@ import org.springframework.web.bind.annotation.RestController;
 @Slf4j
 public class NoticeMsgController {
     private final IAssistantUserBindClient userBindClient;
-
+    private final IAssistantUserClient userClient;
     @PostMapping("/bind/{bindId}")
     @ApiOperation("查询绑定申请消息详情")
-    public R bindMsg(@PathVariable("bindId") String bindId){
-        return R.success(userBindClient.findById(bindId));
+    public R<AssistantUserBindResult> bindMsg(@PathVariable("bindId") String bindId){
+        AssistantUserBindEntity userBind = userBindClient.findById(bindId);
+        AssistantUserBindResult result = BeanUtil.toBean(userBind, AssistantUserBindResult.class);
+        result.setAssistPhone(Optional.ofNullable(userClient.getById(userBind.getAssistId())).orElse(new AssistantUserResult()).getPhone());
+        return R.success(result);
     }
 }

+ 26 - 16
nb-service/app-doctor/src/main/java/com/nb/app/doctor/controller/PatientMonitorController.java

@@ -1,14 +1,13 @@
 package com.nb.app.doctor.controller;
 
-import cn.dev33.satoken.annotation.SaCheckPermission;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollectionUtil;
-import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
 import com.nb.app.assistant.api.bean.HandleBindResult;
 import com.nb.app.assistant.api.entity.AssistantUserBindEntity;
 import com.nb.app.assistant.api.feign.IAssistantUserBindClient;
 import com.nb.app.assistant.api.feign.result.ContactQuery;
+import com.nb.app.assistant.api.feign.result.PatientBindAssistantResult;
 import com.nb.app.assistant.api.feign.result.UpdateBindPatientParam;
 import com.nb.app.doctor.controller.vo.ChangeManageVo;
 import com.nb.app.doctor.controller.vo.InviteCodeVo;
@@ -17,7 +16,6 @@ import com.nb.app.doctor.service.dto.PatientMonitorConsultResult;
 import com.nb.auth.utils.SecurityUtil;
 import com.nb.common.websocket.WebSocketSessionLifeCycleManage;
 import com.nb.core.annotation.Log;
-import com.nb.core.exception.CustomException;
 import com.nb.core.result.R;
 import com.nb.web.api.entity.BusClinicEntity;
 import com.nb.web.api.feign.IPatientClient;
@@ -72,16 +70,16 @@ public class PatientMonitorController {
                             .forEach(bind->{
                                 String patientId = bind.getPatientId();
                                 String assistId = bind.getAssistId();
-                                if (lifeCycleManage.isOnline(assistId)) {
-                                    List<PatientMonitorConsultResult> groupByPatientId = resultGroupByPatientId.get(patientId);
-                                    if (CollectionUtil.isNotEmpty(groupByPatientId)) {
-                                        groupByPatientId
-                                                .forEach(consultResult->{
-                                                    consultResult.setAssistId(assistId);
-                                                    consultResult.setOnline(true);
-                                                });
-                                    }
+//                                if (lifeCycleManage.isOnline(assistId)) {
+                                List<PatientMonitorConsultResult> groupByPatientId = resultGroupByPatientId.get(patientId);
+                                if (CollectionUtil.isNotEmpty(groupByPatientId)) {
+                                    groupByPatientId
+                                            .forEach(consultResult->{
+                                                consultResult.setAssistId(assistId);
+//                                                consultResult.setOnline(true);
+                                            });
                                 }
+//                                }
                             });
                 }
             }
@@ -117,9 +115,9 @@ public class PatientMonitorController {
     @PostMapping("/invite/code")
     @ApiOperation(value = "患者看护邀请码,默认失效失效(7天)")
     public R<InviteCodeResult> inviteCode(@RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId, @RequestBody@Validated InviteCodeVo resource) {
-        if(userBindClient.haveBindByPatientCode(resource.getPatientCode(),tenantId)){
-            throw new CustomException("该病人已被看护");
-        }
+//        if(userBindClient.haveBindByPatientCode(resource.getPatientCode(),tenantId)){
+//            throw new CustomException("该病人已被看护");
+//        }
         return R.success(patientClient.generateInviteCode(tenantId,resource.getPatientCode(),resource.getDoctorId(),resource.getDoctorName(),resource.getManage()));
     }
 
@@ -141,7 +139,7 @@ public class PatientMonitorController {
     @PostMapping("/lift/{bindId}")
     @ApiOperation(value = "与病人解除绑定")
     public R<Boolean> lift(@PathVariable("bindId") String bindId) {
-        return R.success(userBindClient.liftBind(bindId,String.valueOf(SecurityUtil.getId()),null));
+        return R.success(userBindClient.liftBind(bindId,String.valueOf(SecurityUtil.getId()),null,false));
     }
 
     @PostMapping("/pull/async")
@@ -150,4 +148,16 @@ public class PatientMonitorController {
     public DeferredResult<R<BusClinicEntity>> syn(@RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId, @Validated@RequestBody GetPatientInfoVo vo){
         return patientClient.getPatientInfoFromHis(tenantId,vo.getPatientCode(),vo.getTimeout(),false,true);
     }
+
+    @PostMapping("/list/assist/{patientId}")
+    @ApiOperation(value = "查看病人看护人列表")
+    public R<List<PatientBindAssistantResult>> listAssist(@PathVariable("patientId") String patientId) {
+        List<PatientBindAssistantResult> results = userBindClient.listAssist(patientId);
+        if(CollectionUtil.isNotEmpty(results)){
+            for (PatientBindAssistantResult result : results) {
+                result.setAssistOnline(lifeCycleManage.isOnline(result.getAssistId()));
+            }
+        }
+        return R.success(results);
+    }
 }

+ 1 - 1
nb-service/app-doctor/src/main/java/com/nb/app/doctor/service/LocalAppDoctorUserService.java

@@ -229,7 +229,7 @@ public class LocalAppDoctorUserService extends BaseService<AppDoctorUserMapper,
         //判断是否处于勿扰模式
         String imRoomId = entity.getRoomId();
         ImRoomEntity room = roomService.getById(imRoomId);
-        if(!ImStatusEnum.SUCCESS.equals(room.getStatus())){
+        if(!ImStatusEnum.SUCCESS.equals(room.getStatus())&&!ImStatusEnum.WAITING.equals(room.getStatus())){
             return;
         }
         AppUserConsultConfigEntity consultConfig = consultConfigService.getOne(new QueryWrapper<AppUserConsultConfigEntity>().lambda()

+ 85 - 0
nb-service/app-doctor/src/main/java/com/nb/app/doctor/service/dto/AssistantUserBindResult.java

@@ -0,0 +1,85 @@
+package com.nb.app.doctor.service.dto;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.nb.app.assistant.api.enums.ApplyEnum;
+import com.nb.app.assistant.api.enums.BindEnum;
+import com.nb.core.enums.SexEnum;
+import com.nb.web.api.enums.ClinicManageEnum;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AssistantUserBindResult.java
+ * @Description TODO
+ * @createTime 2022年10月19日 13:37:00
+ */
+@Data
+public class AssistantUserBindResult implements Serializable {
+    private static final long serialVersionUID = 1L;
+    private String id;
+
+    @ApiModelProperty(value = "用户id")
+    private String assistId;
+
+    @ApiModelProperty(value = "用户昵称")
+    private String assistNickname;
+
+    @ApiModelProperty(value = "用户手机号")
+    private String assistPhone;
+
+    @ApiModelProperty(value = "病人id,当手动填写数据时,为空;当邀请码填充时,不为空")
+    private String patientId;
+
+    @ApiModelProperty(value = "住院号")
+    private String patientCode;
+
+    @ApiModelProperty(value = "病人姓名")
+    private String patientName;
+
+    @ApiModelProperty(value = "病人性别")
+    private SexEnum patientSex;
+
+    @ApiModelProperty(value = "病人年龄")
+    private Integer patientAge;
+
+    @ApiModelProperty(value = "医生id")
+    private String doctorId;
+
+    @ApiModelProperty(value = "医生名称")
+    private String doctorName;
+
+    @ApiModelProperty(value = "医院id")
+    private String tenantId;
+
+    @ApiModelProperty(value = "医院名称")
+    private String tenantName;
+
+    @ApiModelProperty(value = "管理位置")
+    private ClinicManageEnum manageType;
+
+    @ApiModelProperty(value = "是否为默认看护")
+    private Boolean default_;
+
+    @ApiModelProperty(value = "是否存在聊天室")
+    private Boolean existIm;
+
+    @ApiModelProperty(value = "申请类型",example = " 0(手动申请) 1(邀请码申请)")
+    private ApplyEnum applyType;
+
+    @ApiModelProperty(value = "绑定状态",example = "0(等待医生进行绑定操作) 1(绑定成功) 2(拒绝) 3(自动断开) 4(手动断开)")
+    private BindEnum status;
+
+    @ApiModelProperty(value = "解除绑定人,当与userId相同时,即本人解除绑定关系,当不同时,即由医生解除绑定关系")
+    private String liftedBy;
+
+    @ApiModelProperty("医生拒绝理由")
+    private String refuseReason;
+
+    @ApiModelProperty(hidden = true)
+    @JsonIgnoreProperties
+    private String inviteCode;
+}

+ 8 - 0
nb-service/app-msg/pom.xml

@@ -15,6 +15,14 @@
     <description>内部消息通知模块</description>
 
     <dependencies>
+        <dependency>
+            <groupId>com.tuoren</groupId>
+            <artifactId>app-assistant-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.tuoren</groupId>
+            <artifactId>web-service-api</artifactId>
+        </dependency>
         <dependency>
             <groupId>com.tuoren</groupId>
             <artifactId>app-msg-api</artifactId>

+ 20 - 2
nb-service/app-msg/src/main/java/com/nb/app/msg/entity/AppMsgEntity.java

@@ -4,6 +4,7 @@ import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.nb.app.msg.bean.MsgBean;
 import com.nb.core.entity.GenericEntity;
 import com.nb.app.msg.enums.MsgEnum;
 import com.nb.core.enums.SexEnum;
@@ -41,7 +42,7 @@ public class AppMsgEntity  extends GenericEntity<String> {
     @ApiModelProperty(value = "发送人名称")
     private String senderName;
 
-    @ApiModelProperty(value = "发送人id")
+    @ApiModelProperty(value = "病人姓名")
     @JsonIgnoreProperties
     private String patientName;
 
@@ -80,13 +81,26 @@ public class AppMsgEntity  extends GenericEntity<String> {
         return StrUtil.isEmpty(patientName)?patientCode:patientName;
     }
 
+    public String getPayload() {
+        if(MsgEnum.ASSIST_LIFT.equals(this.getMsgType())
+        ||MsgEnum.DOCTOR_LIFT.equals(this.getMsgType())){
+            return String.format("%s与%s解除绑定",this.getSenderName(),this.getPatient());
+        }
+
+        return payload;
+    }
+
+    public void setPayload(String payload) {
+        this.payload = payload;
+    }
+
     public String getTitle() {
         if(msgType==null){
             return "";
         }
         switch (msgType){
             case CONSUL:
-                return getSenderName()+"发起"+getMsgType()+"咨询";
+                return getSenderName()+"发起"+getExtend()+"咨询";
             case REGISTER:
                 return "您已注册成功,默认密码为A123456,请及时更改";
             case PAIN_CALL:
@@ -101,6 +115,10 @@ public class AppMsgEntity  extends GenericEntity<String> {
                 return getSenderName()+"通过了"+getPatient()+"的绑定申请";
             case ASSIST_EVAL:
                 return  getPatient()+getExtend()+"未进行疼痛评估";
+            case ASSIST_LIFT:
+                return String.format("%s与%s解除绑定",this.getSenderName(),this.getPatient());
+            case DOCTOR_LIFT:
+                return String.format("%s与%s解除绑定",this.getSenderName(),this.getPatient());
             default:  return "";
         }
     }

+ 14 - 3
nb-service/app-msg/src/main/java/com/nb/app/msg/service/LocalAppConsultService.java

@@ -1,5 +1,6 @@
 package com.nb.app.msg.service;
 
+import cn.hutool.core.map.MapUtil;
 import cn.hutool.extra.spring.SpringUtil;
 import com.nb.app.msg.bean.MsgBean;
 import com.nb.app.msg.entity.AppConsultEntity;
@@ -9,8 +10,14 @@ import com.nb.app.msg.mapper.AppConsultMapper;
 import com.nb.auth.bean.LoginUser;
 import com.nb.auth.utils.SecurityUtil;
 import com.nb.common.crud.BaseService;
+import com.nb.web.api.enums.ClinicManageEnum;
+import com.nb.web.api.feign.IPatientClient;
+import lombok.AllArgsConstructor;
 import org.springframework.stereotype.Service;
 
+import javax.validation.constraints.NotNull;
+import java.util.HashMap;
+
 /**
  * @author lifang
  * @version 1.0.0
@@ -19,8 +26,9 @@ import org.springframework.stereotype.Service;
  * @createTime 2022年08月12日 09:48:00
  */
 @Service
+@AllArgsConstructor
 public class LocalAppConsultService extends BaseService<AppConsultMapper, AppConsultEntity,String> {
-
+    private final IPatientClient patientClient;
 
     @Override
     public void validateBeforeSave(AppConsultEntity entity) {
@@ -40,12 +48,13 @@ public class LocalAppConsultService extends BaseService<AppConsultMapper, AppCon
     @Override
     public void postSave(AppConsultEntity entity) {
         LoginUser loginUser = SecurityUtil.getLoginUser();
+        ClinicManageEnum manageType = patientClient.getManageType(entity.getPatientId());
         SpringUtil.publishEvent(new SaveMsgEvent(
                 this,
                 MsgBean.builder()
                         .msgType(MsgEnum.CONSUL)
                         .senderId(String.valueOf(loginUser.getId()))
-                        .senderNickname(loginUser.getUsername())
+                        .senderNickname(loginUser.getNickName())
                         .patientId(entity.getPatientId())
                         .patientName(entity.getPatientName())
                         .patientCode(entity.getPatientCode())
@@ -54,7 +63,9 @@ public class LocalAppConsultService extends BaseService<AppConsultMapper, AppCon
                         .receiverId(entity.getDoctorId())
                         .receiverName(entity.getDoctorNickName())
                         .payload(entity.getId())
-                        .extend(entity.getId())
+                        .extend(entity.getType())
+//                        .extend(entity.getId())
+                        .properties(MapUtil.of("manageType",manageType))
                         .build()
         ));
     }

+ 20 - 0
nb-service/app-msg/src/main/java/com/nb/app/msg/service/LocalAppMsgService.java

@@ -1,11 +1,14 @@
 package com.nb.app.msg.service;
 
 import cn.hutool.core.bean.BeanUtil;
+import com.nb.app.assistant.api.event.AssistLiftEvent;
+import com.nb.app.msg.enums.MsgEnum;
 import com.nb.app.msg.event.SaveMsgEvent;
 import com.nb.common.crud.BaseService;
 import com.nb.app.msg.entity.AppMsgEntity;
 import com.nb.app.msg.mapper.AppMsgMapper;
 import org.springframework.context.event.EventListener;
+import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 
 /**
@@ -40,4 +43,21 @@ public class LocalAppMsgService extends BaseService<AppMsgMapper,AppMsgEntity,St
         source.setExtend(event.getPayload().getExtend());
         this.save(source);
     }
+
+    @EventListener
+    @Async
+    public void lift(AssistLiftEvent event){
+        boolean assistLift = event.isAssistLift();
+        AppMsgEntity msg = new AppMsgEntity();
+        msg.setMsgType(assistLift? MsgEnum.ASSIST_LIFT:MsgEnum.DOCTOR_LIFT);
+        msg.setSenderId(assistLift?event.getAssistId():event.getDoctorId());
+        msg.setSenderName(assistLift?event.getAssistNickname():event.getDoctorNickname());
+        msg.setPatientId(event.getPatientId());
+        msg.setPatientName(event.getPatientName());
+        msg.setPatientCode(event.getPatientCode());
+        msg.setReceiverId(assistLift?event.getDoctorId():event.getAssistId());
+        msg.setReceiverName(assistLift?event.getDoctorNickname():event.getAssistNickname());
+        this.save(msg);
+
+    }
 }

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

@@ -21,6 +21,7 @@ import com.nb.core.result.R;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.Authorization;
 import lombok.AllArgsConstructor;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
@@ -90,18 +91,7 @@ public class BusDeviceHistoryController extends BaseCrudController<BusDeviceHist
                 .lambda()
                 .select(BusDeviceHistoryEntity::getDataNumber)
                 .eq(BusDeviceHistoryEntity::getInfusionId, infusionId));
-        long count = histories.stream().map(BusDeviceHistoryEntity::getDataNumber).distinct().count();
-        AtomicReference<BigDecimal> result=new AtomicReference<>(BigDecimal.ZERO);
-        if(CollectionUtil.isNotEmpty(histories)){
-            histories.stream().map(BusDeviceHistoryEntity::getDataNumber)
-                    .max(Comparator.comparing(Integer::valueOf))
-                    .map(max->{
-                        BigDecimal proportionRate = BigDecimal.valueOf(count).divide(BigDecimal.valueOf(max), 2, BigDecimal.ROUND_HALF_UP);
-                        result.set(BigDecimal.ONE.subtract(proportionRate).multiply(BigDecimal.valueOf(100)));
-                        return max;
-                    });
-        }
-        return R.success(result.get());
+        return R.success(historyService.computeLossRate(histories));
     }
 
     @Override

+ 60 - 15
nb-service/web-service/src/main/java/com/nb/web/service/bus/controller/BusInfusionHistoryController.java

@@ -1,10 +1,9 @@
 package com.nb.web.service.bus.controller;
 
-import cn.dev33.satoken.SaManager;
 import cn.dev33.satoken.annotation.SaCheckPermission;
-import cn.dev33.satoken.stp.StpLogic;
 import cn.hutool.core.collection.CollectionUtil;
 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.baomidou.mybatisplus.core.metadata.IPage;
 import com.nb.web.api.entity.BusDeviceHistoryEntity;
@@ -17,17 +16,19 @@ import com.nb.common.crud.BaseService;
 import com.nb.common.crud.controller.BaseQueryController;
 import com.nb.core.exception.CustomException;
 import com.nb.core.result.R;
+import com.nb.web.service.bus.service.dto.TestBusInfusionHistory;
+import com.nb.web.service.bus.utils.WsPublishUtils;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.Authorization;
 import lombok.AllArgsConstructor;
 import org.springframework.web.bind.annotation.*;
 
 import java.math.BigDecimal;
 import java.util.Arrays;
-import java.util.Comparator;
 import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * @author lifang
@@ -43,6 +44,7 @@ import java.util.concurrent.atomic.AtomicReference;
 public class BusInfusionHistoryController implements BaseQueryController<BusInfusionHistoryEntity, String> {
     private final LocalBusInfusionHistoryService infusionHistoryService;
     private final LocalBusDeviceHistoryService deviceHistoryService;
+    private final WsPublishUtils wsPublishUtils;
 
 
     @Override
@@ -77,17 +79,60 @@ public class BusInfusionHistoryController implements BaseQueryController<BusInfu
                 .lambda()
                 .select(BusDeviceHistoryEntity::getDataNumber)
                 .eq(BusDeviceHistoryEntity::getInfusionId, infusionId));
-        AtomicReference<BigDecimal> result=new AtomicReference<>(BigDecimal.ZERO);
-        if(CollectionUtil.isNotEmpty(histories)){
-            histories.stream().map(BusDeviceHistoryEntity::getDataNumber)
-                    .max(Comparator.comparing(Integer::byteValue))
-                    .map(max->{
-                        BigDecimal proportionRate = BigDecimal.valueOf(max).divide(BigDecimal.valueOf(CollectionUtil.size(histories)), 2, BigDecimal.ROUND_HALF_UP);
-                        result.set(BigDecimal.ONE.subtract(proportionRate).multiply(BigDecimal.valueOf(100)));
-                        return max;
-                    });
+        return R.success(deviceHistoryService.computeLossRate(histories));
+    }
+
+
+    @PostMapping("/test/query/page")
+    @SaCheckPermission("bus:infusion:test")
+    @ApiOperation(value = "输注丢包率列表",notes = "权限:【bus:infusion:test】",authorizations = {@Authorization("bus:infusion:test")})
+    public R<List<TestBusInfusionHistory>> testPage(){
+        List<BusInfusionHistoryEntity> infusions = infusionHistoryService.list(new QueryWrapper<BusInfusionHistoryEntity>()
+                .lambda()
+                .select(BusInfusionHistoryEntity::getId,
+                        BusInfusionHistoryEntity::getDeviceId,
+                        BusInfusionHistoryEntity::getClassification,
+                        BusInfusionHistoryEntity::getDataNumber,
+                        BusInfusionHistoryEntity::getType,
+                        BusInfusionHistoryEntity::getPatientCode,
+                        BusInfusionHistoryEntity::getPatientId)
+                .eq(BusInfusionHistoryEntity::getFinished, false));
+        List<TestBusInfusionHistory> result = infusions
+                .stream()
+                .map(TestBusInfusionHistory::valueOf)
+                .collect(Collectors.toList());
+        if(CollectionUtil.isNotEmpty(result)){
+            List<String> ids = result.stream().map(TestBusInfusionHistory::getId).collect(Collectors.toList());
+            List<BusDeviceHistoryEntity> histories = deviceHistoryService.list(new QueryWrapper<BusDeviceHistoryEntity>()
+                    .lambda()
+                    .select(BusDeviceHistoryEntity::getId,BusDeviceHistoryEntity::getInfusionId,BusDeviceHistoryEntity::getDataNumber)
+                    .in(BusDeviceHistoryEntity::getInfusionId, ids));
+            Map<String, List<BusDeviceHistoryEntity>> historiesMap = histories.stream()
+                    .collect(Collectors.groupingBy(BusDeviceHistoryEntity::getInfusionId));
+            result.parallelStream()
+                    .forEach(infusion->
+                            infusion.setLossRate(deviceHistoryService.computeLossRate(historiesMap.get(infusion.getId())))
+                    );
         }
-        return R.success(result.get());
+        return R.success(result);
+    }
+
+
+    @PostMapping("/test/finished")
+    @SaCheckPermission("bus:infusion:finish")
+    @ApiOperation(value = "结束输注",notes = "权限:【bus:infusion:finish】",authorizations = {@Authorization("bus:infusion:finish")})
+    public R<Boolean> finish(@RequestBody List<String> ids){
+        List<BusInfusionHistoryEntity> infusions = infusionHistoryService.listByIds(ids);
+        if(CollectionUtil.isEmpty(infusions)){
+            return R.success(true);
+        }
+        infusionHistoryService.update(new UpdateWrapper<BusInfusionHistoryEntity>()
+                .lambda()
+                .in(BusInfusionHistoryEntity::getId, ids)
+                .set(BusInfusionHistoryEntity::getFinished, true));
+        infusions
+                .forEach(infusion-> wsPublishUtils.asyncPublishPatientMonitor(infusion.getPatientId(),infusion.getTenantId()));
+        return R.success(true);
     }
 
     @Override

+ 9 - 4
nb-service/web-service/src/main/java/com/nb/web/service/bus/controller/BusPatientController.java

@@ -3,6 +3,7 @@ package com.nb.web.service.bus.controller;
 import cn.dev33.satoken.annotation.SaCheckPermission;
 import cn.dev33.satoken.annotation.SaMode;
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.text.CharSequenceUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
@@ -226,14 +227,18 @@ public class BusPatientController  implements BaseQueryController<BusPatientEnti
     @PostMapping("/{alarm}/_count")
     @SaCheckPermission("device:patient:query")
     @ApiOperation(value = "病人报警数量统计",notes = "病人报警数量统计 0、未报警 1、泵重复 2、无泵,权限标识为【device:patient:query】")
-    public R<Long> alarmCount(@PathVariable("alarm") int alarm ){
+    public R<Integer> alarmCount( @RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId,@PathVariable("alarm") int alarm ){
         PatientAlarmEnum alarmEnum = PatientAlarmEnum.of(alarm);
         if(alarmEnum==null){
-            return R.success(0L);
+            return R.success(0);
         }
         log.info("病人报警数量统计,【{}】",alarmEnum);
-        return R.success(patientService.patientAlarmCount(alarmEnum));
-
+        if (PatientAlarmEnum.DEVICE_NONE.equals(alarmEnum)){
+            return R.success(CollectionUtil.size(patientService.noneDevice()));
+        }else  if (PatientAlarmEnum.DEVICE_REPEAT.equals(alarmEnum)){
+            return R.success(CollectionUtil.size(patientService.repeatDevice()));
+        }
+        return R.success(0);
     }
 
     @PostMapping("/shift")

+ 1 - 0
nb-service/web-service/src/main/java/com/nb/web/service/bus/entity/BusPatientEntity.java

@@ -42,6 +42,7 @@ public class BusPatientEntity extends TenantGenericEntity<String,String> {
     private String clinicId;
 
     @TableField
+    @Deprecated
     @ApiModelProperty(value = "病人报警信息",example = "泵重复,无泵")
     private PatientAlarmEnum alarm;
 

+ 8 - 6
nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/HisScriptSession.java

@@ -184,9 +184,10 @@ public class HisScriptSession {
         }
         DeferredResult<R<BusClinicEntity>> result = new DeferredResult<>(unit.toMillis(timeout));
         if(!online){
-            BusClinicEntity clinic = clinicService.recentClinicByPatientCode(hospitalId, patientCode);
+            BusPatientEntity patient = patientService.getOneByHospitalAndPatientCode(hospitalId, patientCode);
+            BusClinicEntity clinic = clinicService.getById(patient.getClinicId());
             if(clinic==null){
-                result.setResult(R.fail("拉取信息超时,请稍后再试"));
+                result.setResult(R.fail(""));
             }else {
                 result.setResult(R.success(clinic));
             }
@@ -205,9 +206,10 @@ public class HisScriptSession {
                 .build();
         result.onTimeout(()->{
             if(needResult){
-                BusClinicEntity clinic = clinicService.recentClinicByPatientCode(hospitalId, patientCode);
+                BusPatientEntity patient = patientService.getOneByHospitalAndPatientCode(hospitalId, patientCode);
+                BusClinicEntity clinic = clinicService.getById(patient.getClinicId());
                 if(clinic==null){
-                    result.setResult(R.fail("拉取信息超时,请稍后再试"));
+                    result.setResult(R.fail("该住院号不存在"));
                 }else {
                     result.setResult(R.success(clinic));
                 }
@@ -376,12 +378,12 @@ public class HisScriptSession {
                         try {
                             BusClinicEntity clinic = handle(messageId,Value.simple(hisResponse.getContext()).asString(), hisResponse.getPatientCode());
                             if(clinic==null){
-                                result.setResult(R.fail(String.format("号【[%s]】病人数据查询为空",hisResponse.getPatientCode())));
+                                result.setResult(R.fail(String.format("住院号【[%s]】病人数据查询为空",hisResponse.getPatientCode())));
                             }else {
                                 result.setResult(R.success(clinic));
                             }
                         }catch (ScriptException e){
-                            result.setResult(R.fail(String.format("号【[%s]】病人数据查询为空",hisResponse.getPatientCode())));
+                            result.setResult(R.fail(String.format("住院号【[%s]】病人数据查询为空",hisResponse.getPatientCode())));
                         }
 
 

+ 25 - 0
nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusDeviceHistoryService.java

@@ -1,5 +1,6 @@
 package com.nb.web.service.bus.service;
 
+import cn.hutool.core.collection.CollectionUtil;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.nb.web.api.entity.BusDeviceHistoryEntity;
 import com.nb.web.service.bus.mapper.BusDeviceHistoryMapper;
@@ -7,6 +8,11 @@ import com.nb.web.service.bus.service.dto.DeviceHistoryQuery;
 import com.nb.common.crud.BaseService;
 import org.springframework.stereotype.Service;
 
+import java.math.BigDecimal;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
 /**
  * @author lifang
  * @version 1.0.0
@@ -35,4 +41,23 @@ public class LocalBusDeviceHistoryService extends BaseService<BusDeviceHistoryMa
     public IPage<BusDeviceHistoryEntity> pageQuery(DeviceHistoryQuery query){
         return this.baseMapper.pageQuery(query.getPage(),query);
     }
+
+
+    public BigDecimal computeLossRate(List<BusDeviceHistoryEntity> sources) {
+        if(CollectionUtil.isEmpty(sources)){
+            return BigDecimal.ZERO;
+        }
+        long count = sources.stream().map(BusDeviceHistoryEntity::getDataNumber).distinct().count();
+        AtomicReference<BigDecimal> result = new AtomicReference<>(BigDecimal.ZERO);
+        if (CollectionUtil.isNotEmpty(sources)) {
+            sources.stream().map(BusDeviceHistoryEntity::getDataNumber)
+                    .max(Comparator.comparing(Integer::valueOf))
+                    .map(max -> {
+                        BigDecimal proportionRate = BigDecimal.valueOf(count).divide(BigDecimal.valueOf(max), 2, BigDecimal.ROUND_HALF_UP);
+                        result.set(BigDecimal.ONE.subtract(proportionRate).multiply(BigDecimal.valueOf(100)));
+                        return max;
+                    });
+        }
+        return result.get();
+    }
 }

+ 4 - 6
nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusPatientService.java

@@ -284,11 +284,10 @@ public class LocalBusPatientService extends BaseService<BusPatientMapper, BusPat
             clinicService.save(clinic);
         }
         if(recentInfusion!=null){
-            patient.setBindDeviceId(recentInfusion.getId());
+            patient.setInfusionId(recentInfusion.getId());
         }
         return patient;
     }
-
     /**
      * 病人手动更新当前监控的临床信息
      *
@@ -525,10 +524,9 @@ public class LocalBusPatientService extends BaseService<BusPatientMapper, BusPat
                 }
             }
         }
-
-
-        clinic.setPatientCode(patient.getCode());
-
+        if(clinic!=null){
+            clinic.setPatientCode(patient.getCode());
+        }
         result.setInfusion(infusion);
         result.setClinic(clinic);
         return result;

+ 52 - 0
nb-service/web-service/src/main/java/com/nb/web/service/bus/service/dto/TestBusInfusionHistory.java

@@ -0,0 +1,52 @@
+package com.nb.web.service.bus.service.dto;
+
+import com.nb.web.api.entity.BusInfusionHistoryEntity;
+import com.nb.web.api.enums.DeviceTypeEnum;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName TestBusInfusionHistory.java
+ * @Description 输注测试列表
+ * @createTime 2022年10月20日 14:40:00
+ */
+@Data
+public class TestBusInfusionHistory implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private String id;
+
+    private String deviceId;
+
+    private String classification;
+
+    private Integer dataNumber;
+
+    private DeviceTypeEnum type;
+
+    private String patientId;
+
+    private String patientCode;
+
+    private String clinicId;
+
+    private BigDecimal lossRate;
+
+    public static TestBusInfusionHistory valueOf(BusInfusionHistoryEntity source){
+        TestBusInfusionHistory result = new TestBusInfusionHistory();
+        result.setId(source.getId());
+        result.setDeviceId(source.getDeviceId());
+        result.setClassification(source.getClassification());
+        result.setDataNumber(source.getDataNumber());
+        result.setType(source.getType());
+        result.setPatientCode(source.getPatientCode());
+        result.setLossRate(BigDecimal.ZERO);
+        result.setPatientId(source.getPatientId());
+        result.setClinicId(source.getClinicId());
+        return result;
+    }
+}

+ 5 - 0
nb-service/web-service/src/main/java/com/nb/web/service/bus/utils/WsPublishUtils.java

@@ -61,6 +61,11 @@ public class WsPublishUtils implements Serializable{
         }
     }
 
+    @Async
+    public void asyncPublishPatientMonitor(String patientId,String tenantId){
+        this.publishPatientMonitor(patientId,tenantId);
+    }
+
     /**
      * 描述: 推送医院设备状态统计数量
      * @author lifang

+ 2 - 1
nb-service/web-service/src/main/java/com/nb/web/service/system/auth/WebAuthGranter.java

@@ -96,7 +96,7 @@ public class WebAuthGranter implements IAuthGranter {
         }
         log.info("登录用户:{}", source.getUsername());
         // 查询角色列表
-        List<SysRole> sysRoleList = sysRoleService.listSysRoleByUserId(sysUser.getId());
+        List<SysRole> sysRoleList = sysRoleService.listSysRoleByUserId(sysUser.getId(),String.valueOf(sysUser.getTenantId()));
         // 查询权限标识
         Set<String> permissions  = sysMenuService.getPermissionsByUserId(sysUser.getId());
 
@@ -113,6 +113,7 @@ public class WebAuthGranter implements IAuthGranter {
         loginUser.setTenantId(sysUser.getTenantId());
         loginUser.setSuperAdmin(isSuperAdmin(source.getGrantType(),sysRoleList));
         loginUser.setPermissions(permissions);
+        loginUser.setNickName(sysUser.getNickname());
         loginUser.setId(sysUser.getId());
         loginUser.setLoginType(StpTypeEnum.DEFAULT.getText());
         fillUserAgentInfo(loginUser);

+ 2 - 0
nb-service/web-service/src/main/java/com/nb/web/service/system/service/ISysRoleService.java

@@ -107,6 +107,8 @@ public interface ISysRoleService extends IService<SysRole> {
      */
     List<SysRole> listSysRoleByUserId(Long userId);
 
+    List<SysRole> listSysRoleByUserId(Long userId, String tenantId);
+
     /**
      * 下拉列表
      *

+ 21 - 11
nb-service/web-service/src/main/java/com/nb/web/service/system/service/impl/SysRoleServiceImpl.java

@@ -1,6 +1,7 @@
 package com.nb.web.service.system.service.impl;
 
 import cn.dev33.satoken.session.SaSession;
+import cn.dev33.satoken.stp.StpLogic;
 import cn.dev33.satoken.stp.StpUtil;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
@@ -30,6 +31,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
+import java.io.Serializable;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -164,21 +166,23 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
                 return sysRoleMenu;
             }).collect(Collectors.toList()));
         }
+        StpLogic stpLogic = SecurityUtil.getStpLogic();
         //分配菜单后将对应的在线人员进行更新
         List<SysUserRole> userRoleList = sysUserRoleService.list(new QueryWrapper<SysUserRole>().lambda().eq(SysUserRole::getRoleId, req.getRoleId()));
         userRoleList
+                .parallelStream()
                 .forEach(sysUserRole -> {
                     Set<String> permissionsByUserId = sysMenuService.getPermissionsByUserId(sysUserRole.getUserId());
-                    List<String> tokens = SecurityUtil.getStpLogic().getTokenValueListByLoginId(sysUserRole.getUserId());
-                    tokens.parallelStream()
-                            .forEach(token->{
-                                SaSession tokenSessionByToken = SecurityUtil.getStpLogic().getTokenSessionByToken(token);
-                                LoginUser user = (LoginUser) tokenSessionByToken.get(Constants.LOGIN_USER_KEY);
-                                if(user!=null){
-                                    user.setPermissions(permissionsByUserId);
-                                    tokenSessionByToken.set(Constants.LOGIN_USER_KEY,user);
-                                }
-                            });
+                    List<String> tokens =stpLogic.getTokenValueListByLoginId(sysUserRole.getUserId());
+                    tokens.
+                            parallelStream().forEach(token->{
+                        SaSession tokenSessionByToken = stpLogic.getTokenSessionByToken(token);
+                        LoginUser user = (LoginUser) tokenSessionByToken.get(Constants.LOGIN_USER_KEY);
+                        if(user!=null){
+                            user.setPermissions(permissionsByUserId);
+                            tokenSessionByToken.set(Constants.LOGIN_USER_KEY,user);
+                        }
+                    });
                 });
     }
 
@@ -214,7 +218,13 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
 
     @Override
     public List<SysRole> listSysRoleByUserId(Long userId) {
-        List<SysUserRole> sysUserRoleList = sysUserRoleService.list(Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, userId));
+        return listSysRoleByUserId(userId,null);
+    }
+
+    @Override
+    public List<SysRole> listSysRoleByUserId(Long userId, String tenantId) {
+        List<SysUserRole> sysUserRoleList = sysUserRoleService.list(Wrappers.lambdaQuery(SysUserRole.class).eq(SysUserRole::getUserId, userId)
+                .eq(StrUtil.isNotEmpty(tenantId),SysUserRole::getTenantId,tenantId));
         if (sysUserRoleList.isEmpty()) {
             return Lists.newArrayList();
         }

+ 12 - 0
pom.xml

@@ -43,6 +43,8 @@
         <aliyun.iot.verison>7.31.0</aliyun.iot.verison>
         <aliyun.sdk.verison>4.5.6</aliyun.sdk.verison>
         <jms.verison>0.56.0</jms.verison>
+        <aliyun.sms.version>2.0.16</aliyun.sms.version>
+        <aliyun.oss.version>3.15.0</aliyun.oss.version>
     </properties>
 
     <modules>
@@ -71,6 +73,16 @@
     <!-- 依赖管理 -->
     <dependencyManagement>
         <dependencies>
+            <dependency>
+                <groupId>com.aliyun.oss</groupId>
+                <artifactId>aliyun-sdk-oss</artifactId>
+                <version>${aliyun.oss.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.aliyun</groupId>
+                <artifactId>dysmsapi20170525</artifactId>
+                <version>${aliyun.sms.version}</version>
+            </dependency>
             <dependency>
                 <groupId>com.tuoren</groupId>
                 <artifactId>mq-common</artifactId>