Преглед изворни кода

update coffee项目改为nb项目

A17404李放 пре 3 година
родитељ
комит
530e42d081
100 измењених фајлова са 7817 додато и 0 уклоњено
  1. 140 0
      code996.sh
  2. BIN
      nb-admin/src/main/resources/python/xmltodict$py.class
  3. 80 0
      nb-admin/src/test/java/com/nb/admin/AliyunTest.java
  4. 103 0
      nb-admin/src/test/java/com/nb/admin/BusClinicTest.java
  5. 101 0
      nb-admin/src/test/java/com/nb/admin/BusDeviceAlarmTest.java
  6. 89 0
      nb-admin/src/test/java/com/nb/admin/BusDeviceTest.java
  7. 44 0
      nb-admin/src/test/java/com/nb/admin/BusHospitalLogTest.java
  8. 97 0
      nb-admin/src/test/java/com/nb/admin/BusHospitalTest.java
  9. 109 0
      nb-admin/src/test/java/com/nb/admin/BusNetpumpTest.java
  10. 263 0
      nb-admin/src/test/java/com/nb/admin/BusPatientTest.java
  11. 28 0
      nb-admin/src/test/java/com/nb/admin/FileUploadTest.java
  12. 118 0
      nb-admin/src/test/java/com/nb/admin/HisStrategyTest.java
  13. 20 0
      nb-admin/src/test/java/com/nb/admin/SpringBootApplicationTests.java
  14. 28 0
      nb-common/src/main/java/com/nb/common/enums/LinkExternalEnum.java
  15. 59 0
      nb-framework/src/main/java/com/nb/framework/aspect/DataSourceAspect.java
  16. 39 0
      nb-framework/src/main/java/com/nb/framework/aspect/DemoModelAspect.java
  17. 146 0
      nb-framework/src/main/java/com/nb/framework/aspect/LogAspect.java
  18. 52 0
      nb-framework/src/main/java/com/nb/framework/config/ApplicationConfig.java
  19. 108 0
      nb-framework/src/main/java/com/nb/framework/config/DruidConfig.java
  20. 75 0
      nb-framework/src/main/java/com/nb/framework/config/RedissonClientAutoConfiguration.java
  21. 25 0
      nb-framework/src/main/java/com/nb/framework/config/SaConfig.java
  22. 55 0
      nb-framework/src/main/java/com/nb/framework/config/SaTokenConfig.java
  23. 128 0
      nb-framework/src/main/java/com/nb/framework/config/SwaggerConfig.java
  24. 34 0
      nb-framework/src/main/java/com/nb/framework/config/ValidatorConfig.java
  25. 120 0
      nb-framework/src/main/java/com/nb/framework/config/WebAppMvcConfig.java
  26. 33 0
      nb-framework/src/main/java/com/nb/framework/config/aspect/PermissionAutoConfiguration.java
  27. 45 0
      nb-framework/src/main/java/com/nb/framework/config/aspect/PermissionMethodInterceptor.java
  28. 43 0
      nb-framework/src/main/java/com/nb/framework/config/convert/EnumConvertFactory.java
  29. 62 0
      nb-framework/src/main/java/com/nb/framework/config/convert/EnumDeserializer.java
  30. 186 0
      nb-framework/src/main/java/com/nb/framework/config/mybatisplus/MybatisPlusConfig.java
  31. 33 0
      nb-framework/src/main/java/com/nb/framework/config/mybatisplus/TenantIdManager.java
  32. 82 0
      nb-framework/src/main/java/com/nb/framework/config/mybatisplus/handler/CreateAndUpdateMetaObjectHandler.java
  33. 135 0
      nb-framework/src/main/java/com/nb/framework/config/mybatisplus/handler/CustomDataPermissionHandler.java
  34. 77 0
      nb-framework/src/main/java/com/nb/framework/config/properties/DruidProperties.java
  35. 25 0
      nb-framework/src/main/java/com/nb/framework/datasource/DynamicDataSource.java
  36. 40 0
      nb-framework/src/main/java/com/nb/framework/datasource/DynamicDataSourceContextHolder.java
  37. 29 0
      nb-framework/src/main/java/com/nb/framework/satoken/service/CustomStpInterface.java
  38. 163 0
      nb-framework/src/main/java/com/nb/framework/web/exception/GlobalExceptionHandler.java
  39. 61 0
      nb-framework/src/main/java/com/nb/framework/web/service/IUserService.java
  40. 294 0
      nb-framework/src/main/java/com/nb/framework/web/service/impl/UserServiceImpl.java
  41. 228 0
      nb-system/src/main/java/com/nb/aliyun/AliyunConsumerGroupService.java
  42. 204 0
      nb-system/src/main/java/com/nb/aliyun/AliyunIotSubscribeClient.java
  43. 24 0
      nb-system/src/main/java/com/nb/aliyun/PlatformAccount.java
  44. 33 0
      nb-system/src/main/java/com/nb/aliyun/PlatformLog.java
  45. 12 0
      nb-system/src/main/java/com/nb/aliyun/PlatformType.java
  46. 324 0
      nb-system/src/main/java/com/nb/aliyun/sdk/AliyunIotSdk.java
  47. 16 0
      nb-system/src/main/java/com/nb/aliyun/utils/Constants.java
  48. 48 0
      nb-system/src/main/java/com/nb/aliyun/utils/DeviceAlarmUtils.java
  49. 32 0
      nb-system/src/main/java/com/nb/aliyun/utils/DeviceRunStatusUtils.java
  50. 25 0
      nb-system/src/main/java/com/nb/aliyun/utils/DeviceTypeUtils.java
  51. 29 0
      nb-system/src/main/java/com/nb/aliyun/utils/EnumUtils.java
  52. 43 0
      nb-system/src/main/java/com/nb/aliyun/utils/Items.java
  53. 67 0
      nb-system/src/main/java/com/nb/aliyun/utils/PumpParams.java
  54. 36 0
      nb-system/src/main/java/com/nb/aliyun/utils/WarnFlowUtils.java
  55. 18 0
      nb-system/src/main/java/com/nb/bus/bean/AliIotConfig.java
  56. 15 0
      nb-system/src/main/java/com/nb/bus/bean/GeoPoint.java
  57. 24 0
      nb-system/src/main/java/com/nb/bus/bean/Script.java
  58. 16 0
      nb-system/src/main/java/com/nb/bus/constant/ClinicConstant.java
  59. 57 0
      nb-system/src/main/java/com/nb/bus/controller/BusAlarmController.java
  60. 72 0
      nb-system/src/main/java/com/nb/bus/controller/BusAppController.java
  61. 207 0
      nb-system/src/main/java/com/nb/bus/controller/BusClinicController.java
  62. 116 0
      nb-system/src/main/java/com/nb/bus/controller/BusConstantController.java
  63. 161 0
      nb-system/src/main/java/com/nb/bus/controller/BusDeviceController.java
  64. 82 0
      nb-system/src/main/java/com/nb/bus/controller/BusDeviceHistoryController.java
  65. 46 0
      nb-system/src/main/java/com/nb/bus/controller/BusDeviceManualController.java
  66. 49 0
      nb-system/src/main/java/com/nb/bus/controller/BusDeviceRunningController.java
  67. 55 0
      nb-system/src/main/java/com/nb/bus/controller/BusDocController.java
  68. 41 0
      nb-system/src/main/java/com/nb/bus/controller/BusDrugController.java
  69. 57 0
      nb-system/src/main/java/com/nb/bus/controller/BusEvaluationController.java
  70. 42 0
      nb-system/src/main/java/com/nb/bus/controller/BusFormulaController.java
  71. 57 0
      nb-system/src/main/java/com/nb/bus/controller/BusHospitalConfigController.java
  72. 147 0
      nb-system/src/main/java/com/nb/bus/controller/BusHospitalController.java
  73. 41 0
      nb-system/src/main/java/com/nb/bus/controller/BusHospitalLogController.java
  74. 59 0
      nb-system/src/main/java/com/nb/bus/controller/BusInfusionHistoryController.java
  75. 372 0
      nb-system/src/main/java/com/nb/bus/controller/BusPatientController.java
  76. 72 0
      nb-system/src/main/java/com/nb/bus/controller/BusStatsAnalyseController.java
  77. 22 0
      nb-system/src/main/java/com/nb/bus/controller/vo/CauseVo.java
  78. 28 0
      nb-system/src/main/java/com/nb/bus/controller/vo/ClinicEditVo.java
  79. 41 0
      nb-system/src/main/java/com/nb/bus/controller/vo/ClinicStatsVo.java
  80. 24 0
      nb-system/src/main/java/com/nb/bus/controller/vo/DeviceBindVo.java
  81. 34 0
      nb-system/src/main/java/com/nb/bus/controller/vo/DeviceRunningMockVo.java
  82. 26 0
      nb-system/src/main/java/com/nb/bus/controller/vo/ExecScript.java
  83. 24 0
      nb-system/src/main/java/com/nb/bus/controller/vo/GetPatientInfoVo.java
  84. 31 0
      nb-system/src/main/java/com/nb/bus/controller/vo/MonitorDetailVo.java
  85. 30 0
      nb-system/src/main/java/com/nb/bus/controller/vo/MonitorFinishedVo.java
  86. 26 0
      nb-system/src/main/java/com/nb/bus/controller/vo/NoPumpConfigVo.java
  87. 76 0
      nb-system/src/main/java/com/nb/bus/entity/BusAppConfig.java
  88. 128 0
      nb-system/src/main/java/com/nb/bus/entity/BusClinicEntity.java
  89. 37 0
      nb-system/src/main/java/com/nb/bus/entity/BusConAlarmEntity.java
  90. 53 0
      nb-system/src/main/java/com/nb/bus/entity/BusConDoctor.java
  91. 48 0
      nb-system/src/main/java/com/nb/bus/entity/BusConMixEntity.java
  92. 186 0
      nb-system/src/main/java/com/nb/bus/entity/BusDeviceAlarmEntity.java
  93. 112 0
      nb-system/src/main/java/com/nb/bus/entity/BusDeviceEntity.java
  94. 62 0
      nb-system/src/main/java/com/nb/bus/entity/BusDeviceHistoryEntity.java
  95. 86 0
      nb-system/src/main/java/com/nb/bus/entity/BusDeviceManualEntity.java
  96. 207 0
      nb-system/src/main/java/com/nb/bus/entity/BusDeviceRunningEntity.java
  97. 32 0
      nb-system/src/main/java/com/nb/bus/entity/BusDocEntity.java
  98. 38 0
      nb-system/src/main/java/com/nb/bus/entity/BusDrugEntity.java
  99. 131 0
      nb-system/src/main/java/com/nb/bus/entity/BusEvaluationEntity.java
  100. 39 0
      nb-system/src/main/java/com/nb/bus/entity/BusFormulaEntity.java

+ 140 - 0
code996.sh

@@ -0,0 +1,140 @@
+#!/usr/bin/env bash
+
+Help()
+{
+   echo "你也可以使用自定义参数进行指定查询"
+   echo
+   echo "格式: bash $0 [2022-01-01] [2022-06-24] [author]"
+   echo "示例: bash code996.sh 2022-01-01 2022-06-24 digua"
+   echo "参数:"
+   echo "1st     分析的起始时间."
+   echo "2nd     分析的结束时间."
+   echo "3rd     指定提交用户,可以是 name 或 email."
+   echo
+}
+
+OS_DETECT()
+{
+   # Detect OS
+    case "$(uname -s)" in
+
+    Linux)
+        # echo 'Linux'
+        open_url="xdg-open"
+        ;;
+
+    Darwin)
+        # echo 'macOS'
+        open_url="open"
+        ;;
+
+    CYGWIN*|MINGW32*|MSYS*|MINGW*)
+        # echo 'Windows'
+        open_url="start"
+        ;;
+
+    *)
+        echo 'Other OS'
+        echo "trying to use xdg-open to open the url"
+        open_url="xdg-open"
+        ;;
+    esac
+
+}
+OS_DETECT
+
+
+time_start=$1
+
+
+if [ "$1" == "--help" ]
+    then
+        Help
+        exit 0
+elif [ "$1" == "-h" ]
+    then
+        Help
+        exit 0
+fi
+
+if [ -z $1 ]
+    then
+        time_start="2022-01-01"
+fi
+
+time_end=$2
+if [ -z $2 ]
+    then
+        time_end=$(date "+%Y-%m-%d")
+fi
+
+author=$3
+if [ -z $3 ]
+    then
+        author=""
+fi
+
+
+by_day_output=`git -C $PWD log --author=$author --date=format:%u --after="$time_start" --before="$time_end" |grep "Date:"|awk '{print $2}'|sort|uniq -c`
+
+by_hour_output=`git -C $PWD log --author=$author --date=format:%H --after="$time_start" --before="$time_end" |grep "Date:"|awk '{print $2}'|sort|uniq -c`
+
+for i in "${by_day_output[@]}"
+    do
+        by_day_result=`echo "$i"|sed -E 's/^ +//g'|sed 's/ /_/g'|tr '\n' ','`
+
+    done
+
+
+# should modify by day format %a or %A
+# day_sorted=('Monday' 'Tuesday' 'Wednesday' 'Thursday' 'Friday' 'Saturday' 'Sunday')
+# day_sorted=('Mon' 'Tue' 'Wed' 'Thu' 'Fri' 'Sat' 'Sun')
+
+RED='\033[1;91m'
+NC='\033[0m' # No Color
+
+echo -e "${RED}统计时间范围:$time_start 至 $time_end"
+
+for i in "${by_day_output[@]}"
+    do
+        echo
+        echo -e "${NC}一周七天 commit 分布${RED}"
+        echo -e "  总提交次数 星期\n$i"|column -t
+        by_day_result=`echo "$i"|sed -E 's/^ +//g'|sed "s/ /_/g"|tr '\n' ','`
+    done
+
+
+for i in "${by_hour_output[@]}"
+    do
+        echo
+        echo -e "${NC}24小时 commit 分布${RED}"
+        echo -e "  总提交次数 小时\n$i"|column -t
+        by_hour_result=`echo "$i"|sed -E 's/^ +//g'|sed "s/ /_/g"|tr '\n' ','`
+    done
+
+
+by_day_result=`echo "$by_day_result"|sed -E 's/,$//g'`
+
+by_hour_result=`echo "$by_hour_result"|sed -E 's/,$//g'`
+
+
+result=$time_start"_"$time_end"&week="$by_day_result"&hour="$by_hour_result
+
+# url
+github_url="https://hellodigua.github.io/code996/#/result?time=$result"
+vercel_url="https://code996.vercel.app/#/result?time=$result"
+gitee_url="https://hellodigua.gitee.io/code996/#/result?time=$result"
+
+echo
+echo -e "${NC}复制以下url以查看可视化分析结果:"
+echo -e "${RED}$github_url"
+echo -e "${NC}"
+echo -e "${NC}若 GitHub 访问过慢,也可以访问以下镜像链接:"
+echo -e "${NC}Vercel节点:"
+echo -e "${RED}$vercel_url"
+echo -e "${NC}"
+echo -e "${NC}Gitee节点:"
+echo -e "${RED}$gitee_url"
+echo -e "${NC}"
+
+$open_url "$github_url"

BIN
nb-admin/src/main/resources/python/xmltodict$py.class


+ 80 - 0
nb-admin/src/test/java/com/nb/admin/AliyunTest.java

@@ -0,0 +1,80 @@
+package com.nb.admin;
+
+import com.aliyuncs.iot.model.v20180120.QueryDeviceResponse;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.nb.aliyun.sdk.AliyunIotSdk;
+import com.nb.bus.entity.BusDeviceEntity;
+import com.nb.bus.entity.BusDeviceRunningEntity;
+import com.nb.bus.entity.BusInfusionHistoryEntity;
+import com.nb.bus.listener.event.bean.DeviceInfoEvent;
+import com.nb.bus.service.LocalBusDeviceService;
+import com.nb.bus.service.LocalBusInfusionHistoryService;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.ApplicationContext;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.List;
+
+/**
+ * @Author longsanlang
+ * @Date 2022-04-06 17:55:44
+ * @Version 1.0
+ * @Description XXX
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AdminApplication.class)
+public class AliyunTest {
+
+    @Autowired
+    private ApplicationContext applicationContext;
+
+    @Autowired
+    private LocalBusInfusionHistoryService localBusInfusionHistoryService;
+
+    @Autowired
+    private LocalBusDeviceService deviceService;
+    @Autowired
+    private LocalBusInfusionHistoryService infusionHistoryService;
+
+    @Autowired
+    private AliyunIotSdk aliyunIotSdk;
+
+    @Test
+    public void test001(){
+        BusDeviceRunningEntity pump = new BusDeviceRunningEntity();
+        pump.setAlias("123456777");
+        DeviceInfoEvent event = new DeviceInfoEvent(this,pump,"123");
+        applicationContext.publishEvent(event);
+
+        try {
+            Thread.sleep(2000000);
+        } catch (InterruptedException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Test
+    public void test002(){
+        List<BusDeviceEntity> deviceList = deviceService.list();
+        for (BusDeviceEntity deviceEntity : deviceList) {
+            BusInfusionHistoryEntity one = infusionHistoryService.getOne(new QueryWrapper<BusInfusionHistoryEntity>()
+                    .lambda().eq(BusInfusionHistoryEntity::getDeviceId, deviceEntity.getDeviceId())
+                    .orderByDesc(BusInfusionHistoryEntity::getStartTime)
+                    .last("limit 1"));
+            if(one!=null){
+                deviceEntity.setInfusionId(one.getId());
+            }
+        }
+        deviceService.updateBatchById(deviceList);
+    }
+
+    @Test
+    public void test003(){
+        List<QueryDeviceResponse.DeviceInfo> deviceInfos = aliyunIotSdk.queryDevice();
+        System.out.println(deviceInfos.size());
+    }
+
+}

+ 103 - 0
nb-admin/src/test/java/com/nb/admin/BusClinicTest.java

@@ -0,0 +1,103 @@
+package com.nb.admin;
+
+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.bus.controller.vo.ClinicStatsVo;
+import com.nb.bus.entity.BusClinicEntity;
+import com.nb.bus.entity.BusDocEntity;
+import com.nb.bus.entity.BusInfusionHistoryEntity;
+import com.nb.bus.service.LocalBusClinicService;
+import com.nb.bus.service.LocalBusDocService;
+import com.nb.bus.service.LocalBusInfusionHistoryService;
+import com.nb.bus.service.dto.ClinicStatsReturnResult;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.redisson.api.RBlockingQueue;
+import org.redisson.api.RDelayedQueue;
+import org.redisson.api.RedissonClient;
+import org.redisson.codec.JsonJacksonCodec;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Author longsanlang
+ * @Date 2022-04-06 17:55:44
+ * @Version 1.0
+ * @Description XXX
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AdminApplication.class)
+public class BusClinicTest {
+
+    @Autowired
+    private RedissonClient redissonClient;
+    @Autowired
+    private LocalBusClinicService clinicService;
+    @Autowired
+    private ObjectMapper objectMapper;
+    @Autowired
+    private LocalBusInfusionHistoryService infusionHistoryService;
+@Autowired
+private LocalBusDocService docService;
+    @Test
+    public void stats(){
+        ClinicStatsVo clinicStatsVo = new ClinicStatsVo();
+        clinicStatsVo.setContinueDose(true);
+        clinicStatsVo.setInValidCount(true);
+        clinicStatsVo.setValidCount(true);
+        clinicStatsVo.setClinicId("1519516923748319234");
+        clinicStatsVo.setDeviceId("456");
+        ClinicStatsReturnResult stats = clinicService.stats(clinicStatsVo);
+        System.out.println(stats);
+    }
+
+    @Test
+    public void redisson(){
+        RBlockingQueue<Object> blockingFairQueue = redissonClient.getBlockingQueue("delay_queue",new JsonJacksonCodec());
+        RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingFairQueue);
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.putOpt(DateUtil.now(),"1");
+        System.out.println(jsonObject);
+
+        delayedQueue.offerAsync(jsonObject,60, TimeUnit.SECONDS);
+        while (true){
+
+        }
+    }
+
+    @Test
+    public void save(){
+        List<Map<String, Object>> mapList = infusionHistoryService
+                .listMaps(new QueryWrapper<BusInfusionHistoryEntity>()
+                        .select("clinic_id","count(1) as c")
+                        .lambda()
+                        .groupBy(BusInfusionHistoryEntity::getClinicId));
+        System.out.println(mapList);
+    }
+
+
+    @Test
+    @Transactional(rollbackFor = ArithmeticException.class)
+    public void test(){
+        clinicService.update(new UpdateWrapper<BusClinicEntity>().lambda().eq(BusClinicEntity::getId,"1528668113304244226")
+        .set(BusClinicEntity::getSurgeryName,"临床数据5"));
+        docService.update(new UpdateWrapper<BusDocEntity>().lambda().eq(BusDocEntity::getId,"1512720050274963458")
+        .set(BusDocEntity::getType,"about1"));
+        int i=1/0;
+    }
+
+
+    public void publish(){
+
+    }
+
+}

+ 101 - 0
nb-admin/src/test/java/com/nb/admin/BusDeviceAlarmTest.java

@@ -0,0 +1,101 @@
+package com.nb.admin;
+
+import com.nb.bus.controller.BusDeviceHistoryController;
+import com.nb.bus.entity.BusDeviceAlarmEntity;
+import com.nb.bus.entity.BusInfusionHistoryEntity;
+import com.nb.bus.enums.DeviceStatusEnum;
+import com.nb.bus.mapper.BusDeviceAlarmMapper;
+import com.nb.bus.service.LocalBusDeviceAlarmService;
+import com.nb.bus.service.LocalBusInfusionHistoryService;
+import com.nb.bus.service.dto.DeviceAlarmQuery;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author zsl
+ * @version 1.0.0
+ * @ClassName BusDeviceAlarmTest.java
+ * @Description TODO
+ * @createTime 2022/5/218:42
+ */
+@RunWith(SpringRunner.class)
+@Slf4j
+@SpringBootTest(classes = AdminApplication.class)
+@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
+public class BusDeviceAlarmTest {
+
+    @Autowired
+    BusDeviceAlarmMapper busDeviceAlarmMapper;
+    @Autowired
+    LocalBusDeviceAlarmService service;
+    @Autowired
+    BusDeviceHistoryController controller;
+    @Autowired
+    LocalBusInfusionHistoryService infusionHistoryService;
+    @Test
+    public void test001(){
+        DeviceAlarmQuery query = new DeviceAlarmQuery();
+        System.out.println(        busDeviceAlarmMapper.selectAlarmCount(query));
+        System.out.println(busDeviceAlarmMapper.selectWarnCount(query));
+    }
+    @Test
+    public void test002(){
+        System.out.println(service.selectCountAlarm(new DeviceAlarmQuery()));
+    }
+
+
+    @Test
+    public void test003(){
+        DeviceAlarmQuery query = new DeviceAlarmQuery();
+        query.setHositalName("驼人医疗器械有限公司");
+        //   query.setTenantId("1");
+        System.out.println(controller.deviceUse("1",query));
+    }
+
+    @Test
+    public void test004(){
+
+        BusDeviceAlarmEntity busDeviceAlarmEntity = new BusDeviceAlarmEntity();
+//        busDeviceAlarmEntity.setAlarm(Boolean.TRUE);
+        // busDeviceAlarmEntity.setAlarmState(DeviceAlarmEnum.Bubble);
+        //busDeviceAlarmEntity.setWarnAnalgesicPoor(1==1);
+        busDeviceAlarmEntity.setRunState(DeviceStatusEnum.NoSignal);
+//        busDeviceAlarmEntity.setWarnFlow(FlowStatusEnum.Lowest);
+        System.out.println(busDeviceAlarmMapper.selectDeviceCount(busDeviceAlarmEntity));
+    }
+    @Test
+    public void test005(){
+
+        BusDeviceAlarmEntity busDeviceAlarmEntity = new BusDeviceAlarmEntity();
+//        busDeviceAlarmEntity.setAlarm(Boolean.TRUE);
+        // busDeviceAlarmEntity.setAlarmState(DeviceAlarmEnum.Bubble);
+        //busDeviceAlarmEntity.setWarnAnalgesicPoor(1==1);
+        busDeviceAlarmEntity.setRunState(DeviceStatusEnum.NoSignal);
+        //busDeviceAlarmEntity.setWarnFlow(FlowStatusEnum.Lowest);
+        System.out.println(service.selectUseDetail(busDeviceAlarmEntity));
+    }
+    @Test
+    public void test006(){
+        ArrayList<BusDeviceAlarmEntity> objects = new ArrayList<>();
+        List<BusDeviceAlarmEntity> list = service.list();
+        for (BusDeviceAlarmEntity deviceAlarmEntity : list) {
+            BusInfusionHistoryEntity byId = infusionHistoryService.getById(deviceAlarmEntity.getInfusionId());
+            if(byId==null){
+                service.removeById(deviceAlarmEntity);
+                continue;
+            }
+            objects.add(deviceAlarmEntity);
+            deviceAlarmEntity.setDeviceType(byId.getType());
+        }
+        service.updateBatchById(objects);
+    }
+}

+ 89 - 0
nb-admin/src/test/java/com/nb/admin/BusDeviceTest.java

@@ -0,0 +1,89 @@
+package com.nb.admin;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.nb.bus.entity.BusDeviceEntity;
+import com.nb.bus.entity.BusDeviceHistoryEntity;
+import com.nb.bus.service.LocalBusDeviceHistoryService;
+import com.nb.bus.service.LocalBusDeviceService;
+import com.nb.bus.service.dto.ClinicAnalInfusionRecord;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @Author XX
+ * @Date 2022-04-25 19:00:10
+ * @Version 1.0
+ * @Description XXX
+ */
+@RunWith(SpringRunner.class)
+@Slf4j
+@SpringBootTest(classes = AdminApplication.class)
+public class BusDeviceTest {
+
+    @Autowired
+    LocalBusDeviceService deviceService;
+
+    @Autowired
+    LocalBusDeviceHistoryService historyService;
+    @Test
+    public void test001(){
+        BusDeviceEntity device = new BusDeviceEntity();
+        device.setDeviceId("12345678910");
+        device.setCreateBy("auto");
+        device.setCreateTime(new Date());
+        deviceService.saveDevice(device);
+
+    }
+
+    @Test
+    public void addAlias(){
+        List<BusDeviceEntity> list = deviceService.list();
+        for (BusDeviceEntity device : list) {
+
+            device.setAlias(BusPatientTest.getName());
+        }
+        deviceService.updateBatchById(list);
+
+    }
+
+    @Test
+    public void test002(){
+        deviceService.removeByDeviceId("nbceshi001");
+    }
+
+
+    @Test
+    public void copy(){
+        Page<BusDeviceHistoryEntity> page = historyService.page(Page.of(0, 50));
+        List<BusDeviceHistoryEntity> records = page.getRecords();
+
+        for (int i = 0; i < 100; i++) {
+            long current = System.currentTimeMillis();
+            records.stream().forEach(record->{
+//            long start = System.currentTimeMillis();
+                BeanUtil.copyProperties(record, ClinicAnalInfusionRecord.class);
+//            System.out.println("BeanCopy耗时:" + (System.currentTimeMillis() - start));
+            });
+            System.out.println("BeanCopy单线程总耗时:" + (System.currentTimeMillis() - current));
+        }
+
+        for (int i = 0; i < 100; i++) {
+            long current1 = System.currentTimeMillis();
+            records.parallelStream().forEach(record->{
+//            long start = System.currentTimeMillis();
+                BeanUtil.copyProperties(record, ClinicAnalInfusionRecord.class);
+//            System.out.println("BeanCopy耗时:" + (System.currentTimeMillis() - start));
+            });
+            System.out.println("BeanCopy多线程总耗时:" + (System.currentTimeMillis() - current1));
+        }
+
+    }
+}

+ 44 - 0
nb-admin/src/test/java/com/nb/admin/BusHospitalLogTest.java

@@ -0,0 +1,44 @@
+package com.nb.admin;
+
+import com.nb.bus.controller.BusHospitalLogController;
+import com.nb.bus.entity.BusHospitalLogEntity;
+import com.nb.bus.service.LocalBusHospitalLogService;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.List;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalTest.java
+ * @Description TODO
+ * @createTime 2022年03月19日 10:27:00
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AdminApplication.class)
+public class BusHospitalLogTest {
+    @Autowired
+    private LocalBusHospitalLogService logService;
+
+    @Autowired
+    private BusHospitalLogController logController;
+    @Test
+    public void save(){
+//        BusHospitalLogEntity logEntity = new BusHospitalLogEntity();
+//        logEntity.setIp("192.168.100.32");
+//        logEntity.setReceiveTime(new Date());
+//        logEntity.setResult("success");
+//        logEntity.setType(HospitalLogEnum.HEART);
+//        logService.save(logEntity);
+    }
+
+    @Test
+    public void query(){
+        List<BusHospitalLogEntity> list = logService.list();
+        System.out.println(list);
+    }
+}

+ 97 - 0
nb-admin/src/test/java/com/nb/admin/BusHospitalTest.java

@@ -0,0 +1,97 @@
+package com.nb.admin;
+
+import com.nb.bus.bean.GeoPoint;
+import com.nb.bus.bean.Script;
+import com.nb.bus.controller.BusHospitalController;
+import com.nb.bus.entity.BusHospitalEntity;
+import com.nb.bus.service.LocalBusHospitalService;
+import com.nb.bus.service.LocalBusPatientService;
+import com.nb.bus.service.constant.LocalBusConMixService;
+import com.nb.bus.service.dto.MonitorStatusStatsCountResult;
+import com.nb.bus.utils.CodeUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.List;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalTest.java
+ * @Description TODO
+ * @createTime 2022年03月19日 10:27:00
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AdminApplication.class)
+public class BusHospitalTest {
+    @Autowired
+    private LocalBusHospitalService busHospitalService;
+    @Autowired
+    private LocalBusPatientService patientService;
+    @Autowired
+    private LocalBusConMixService conMixService;
+    @Autowired
+    private BusHospitalController busHospitalController;
+    @Test
+    public void save(){
+//        StpUtil.login();
+        BusHospitalEntity busHospitalEntity = new BusHospitalEntity();
+        busHospitalEntity.setAddress("河南长垣");
+        busHospitalEntity.setAreaCode("453400");
+
+        GeoPoint geoPoint = new GeoPoint();
+        geoPoint.setLat("101");
+        geoPoint.setLon("1010");
+        busHospitalEntity.setCoordinate(geoPoint);
+        busHospitalEntity.setName("人民医院");
+        busHospitalEntity.setTelephone("18339543637");
+        busHospitalEntity.setEmail("493216081@qq.com");
+//        busHospitalController.add(busHospitalEntity);
+    }
+
+    @Test
+    public void query(){
+        List<BusHospitalEntity> list = busHospitalService.list();
+        list.forEach(busHospitalEntity -> busHospitalEntity.setCode(CodeUtils.genInviteCode(Long.valueOf(busHospitalEntity.getId()))));
+        busHospitalService.updateBatchById(list);
+    }
+
+    @Test
+    public void Delete(){
+        boolean b = busHospitalService.removeById(1505789328745721857L);
+        System.out.println(b);
+    }
+
+    @Test
+    public void script(){
+        String id="";
+        String str = "# -*- coding: utf-8 -*-\n" +
+                "import sys\n" +
+                "reload(sys)\n" +
+                "sys.setdefaultencoding('utf-8')\n" +
+                "import json\n" +
+                "import xmltodict as xmltodict\n" +
+                "\n" +
+                "\n" +
+                "def parse(str):\n" +
+                "    json_dict = xmltodict.parse(str, encoding='utf-8')\n" +
+                "    json_str = json.dumps(json_dict, indent=2)\n" +
+                "    return json_str";
+        BusHospitalEntity busHospitalEntity = new BusHospitalEntity();
+        busHospitalEntity.setId("1505789859765604353");
+        Script script = new Script();
+        script.setId("python");
+        script.setContent(str);
+        busHospitalEntity.setScript(script);
+        busHospitalService.updateById(busHospitalEntity);
+    }
+
+    @Test
+    public void debug(){
+        MonitorStatusStatsCountResult monitorStatusStatsCountResult = patientService.statusStats("1");
+//        conMixService.insertUniqueCon(ConstantMixEnum.ward, Arrays.asList("手术名称1","手术名称2","手术3").stream().collect(Collectors.toSet()), "1");
+    }
+}

+ 109 - 0
nb-admin/src/test/java/com/nb/admin/BusNetpumpTest.java

@@ -0,0 +1,109 @@
+package com.nb.admin;
+
+import cn.hutool.core.util.RandomUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.nb.bus.entity.BusDeviceEntity;
+import com.nb.bus.service.LocalBusDeviceService;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.List;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalTest.java
+ * @Description TODO
+ * @createTime 2022年03月19日 10:27:00
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AdminApplication.class)
+public class BusNetpumpTest {
+
+    @Autowired
+    private LocalBusDeviceService deviceService;
+
+
+    @Autowired
+    private ObjectMapper objectMapper;
+
+
+    @Test
+    public void json() throws JsonProcessingException {
+        BusDeviceEntity device = new BusDeviceEntity();
+        System.out.println(objectMapper.writeValueAsString(device));
+
+    }
+    @Test
+    public void save(){
+//        StpUtil.login();
+        BusDeviceEntity deviceEntity = deviceService.getOne(new QueryWrapper<BusDeviceEntity>().last("limit 1"));
+//        netPumpEntity.setWarn(DeviceWarnEnum.ComingEnd);
+        for (int i = 0; i < 2; i++) {
+            deviceEntity.setId(null);
+            deviceEntity.setAlias("");
+            deviceEntity.setDeviceId(RandomUtil.randomString(5));
+            deviceEntity.setTenantId("1505808170691784706");
+            deviceService.save(deviceEntity);
+        }
+
+    }
+
+    @Test
+    public void query(){
+        List<BusDeviceEntity> list = deviceService.list(new QueryWrapper<BusDeviceEntity>().lambda().eq(BusDeviceEntity::getTenantId,"1"));
+
+//        BusDeviceRunningEntity runningEntity = deviceRunningService.getOne(new QueryWrapper<BusDeviceRunningEntity>().last("limit 1"));
+//        for (int i = 0; i < 2000; i++) {
+//            for (BusDeviceEntity deviceEntity : list) {
+//                deviceEntity.setDeviceId(RandomUtil.randomString(5));
+//                deviceEntity.setId(null);
+//                deviceEntity.setMqttConnInfo("1");
+//                deviceService.save(deviceEntity);
+//
+//                runningEntity.setDataNumber( runningEntity.getDataNumber());
+//                runningEntity.setDeviceId(deviceEntity.getDeviceId());
+//                runningEntity.setHistoryId(deviceEntity.getTenantId());
+//                runningEntity.setTenantId("1");
+//                runningEntity.setUploadTime(DateUtil.yesterday());
+//                runningEntity.setId(null);
+//                try {
+//                    SpringUtil.publishEvent(new DeviceInfoEvent(this,runningEntity,runningEntity.getDeviceId()));
+//                }catch (Exception e){
+//                    e.printStackTrace();
+//                }
+//
+//            }
+//        }
+//        while (true){
+//
+//        }
+//        BusDeviceEntity deviceEntity = new BusDeviceEntity();
+//        deviceEntity.setAlias("456");
+//        deviceEntity.setType(DeviceTypeEnum.intelligent);
+//        deviceEntity.setDeviceId("456");
+//        deviceEntity.setEnable(true);
+//        deviceEntity.setTenantId("1505808170691784706");
+//        BusDeviceEntity deviceEntity1 = new BusDeviceEntity();
+//        deviceEntity1.setAlias("123");
+//        deviceEntity1.setType(DeviceTypeEnum.continuous);
+//        deviceEntity1.setDeviceId("123");
+//        deviceEntity1.setTenantId("1505808170691784706");
+//        deviceEntity1.setEnable(true);
+//        deviceService.save(deviceEntity);
+//        deviceService.save(deviceEntity1);
+//        List<BusDeviceRunningEntity> list = netPumpService.list();
+//        System.out.println(list);
+
+    }
+
+    @Test
+    public void queryRegistry(){
+
+    }
+}

+ 263 - 0
nb-admin/src/test/java/com/nb/admin/BusPatientTest.java

@@ -0,0 +1,263 @@
+package com.nb.admin;
+
+import cn.hutool.core.date.DateField;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.EnumUtil;
+import cn.hutool.core.util.RandomUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.nb.bus.controller.BusDeviceManualController;
+import com.nb.bus.controller.vo.ClinicEditVo;
+import com.nb.bus.entity.BusClinicEntity;
+import com.nb.bus.entity.BusDeviceManualEntity;
+import com.nb.bus.entity.BusInfusionHistoryEntity;
+import com.nb.bus.entity.BusPatientEntity;
+import com.nb.bus.enums.DeviceManualEnum;
+import com.nb.bus.hospital.his.strategy.all.EqualsStrategyHandler;
+import com.nb.bus.service.LocalBusClinicService;
+import com.nb.bus.service.LocalBusInfusionHistoryService;
+import com.nb.bus.service.LocalBusPatientService;
+import com.nb.bus.service.dto.PatientDeviceRepeatResult;
+import com.nb.common.enums.SexEnum;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.io.UnsupportedEncodingException;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusPatientTest.java
+ * @Description TODO
+ * @createTime 2022年04月19日 15:08:00
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AdminApplication.class)
+public class BusPatientTest {
+    @Autowired
+    private LocalBusPatientService patientService;
+
+    @Autowired
+    private LocalBusClinicService clinicService;
+
+    @Autowired
+    private LocalBusInfusionHistoryService infusionHistoryService;
+
+    @Autowired
+    private EqualsStrategyHandler equalsStrategyHandler;
+    @Autowired
+    private BusDeviceManualController manualController;
+    @Test
+    public  void test(){
+        List<PatientDeviceRepeatResult> patientDeviceRepeatResults = patientService.repeatDevice();
+        System.out.println(getName());
+        System.out.println(patientDeviceRepeatResults);
+    }
+
+    public static List<String> clinicNames= Arrays.asList("阑尾手术","肛门手术","肝脏手术","感冒","发烧","肠其他手术","结肠造口术","回肠造口术","肠造口闭合术","肠固定术","肠的其他修补","阑尾切除术","腹腔镜下阑尾切除术","阑尾切除术","阑尾残端切除术");
+    public static List<String> anaTypes=Arrays.asList("局部麻醉","全麻");
+    public static List<String> analTypes=Arrays.asList("PCIA","PCEA","PCSA","PCNA");
+
+    @Test
+    public void save(){
+        for (int i = 0; i < 30; i++) {
+            BusClinicEntity clinic = new BusClinicEntity();
+            clinic.setStartTime(new Date());
+            clinic.setSurgeryName(clinicNames.get(RandomUtil.randomInt(clinicNames.size()-1)));
+            clinic.setPatientName(getName());
+            clinic.setPatientGender(EnumUtil.likeValueOf(SexEnum.class,RandomUtil.randomInt(1,2)));
+            clinic.setBedNo(String.valueOf(RandomUtil.randomInt(10,100)));
+            clinic.setWard(String.valueOf(RandomUtil.randomInt(10,100)));
+            clinic.setWeight(String.valueOf(RandomUtil.randomInt(90,150)));
+            clinic.setWeight(String.valueOf(RandomUtil.randomInt(150,200)));
+            clinic.setAnaDoctor(getName());
+            clinic.setAnaType(anaTypes.get(RandomUtil.randomInt(anaTypes.size()-1)));
+            clinic.setAnalType(analTypes.get(RandomUtil.randomInt(analTypes.size()-1)));
+            clinic.setSurgeryDoctor(getName());
+            clinic.setConfigPerson(getName());
+            clinic.setPatientCode(String.valueOf(RandomUtil.randomInt(100000,300000)));
+            clinic.setMonitorType(true);
+            clinic.setTenantId("1");
+            BusPatientEntity patient = BusPatientEntity.of(clinic);
+//        patientService.save(patient);
+//        clinicService.save(clinic);
+            patientService.manualEdit(clinic);
+        }
+    }
+
+    @Test
+    public void fill(){
+        List<BusClinicEntity> clinicList = clinicService.list(new QueryWrapper<BusClinicEntity>().lambda().eq(BusClinicEntity::getMonitorType, true));
+        Map<String, List<BusClinicEntity>> groupByHospital = clinicList.stream().collect(Collectors.groupingBy(BusClinicEntity::getTenantId));
+        groupByHospital.forEach((tenantId,list)->{
+            Map< String, List<BusClinicEntity>> groupByPatientCode = list.stream().collect(Collectors.groupingBy(BusClinicEntity::getPatientCode));
+            groupByPatientCode.forEach((k,targets)->{
+                List<BusClinicEntity> source = new ArrayList<>();
+                for (BusClinicEntity target : targets) {
+                    BusClinicEntity clinic = new BusClinicEntity();
+                    clinic.setStartTime(target.getStartTime()==null?
+                            RandomUtil.randomDate(DateUtil.beginOfMonth(new Date()), DateField.HOUR,-30,30):target.getStartTime());
+                    clinic.setSurgeryName(clinicNames.get(RandomUtil.randomInt(clinicNames.size()-1)));
+                    clinic.setPatientName(getName());
+                    clinic.setPatientGender(EnumUtil.likeValueOf(SexEnum.class,RandomUtil.randomInt(1,2)));
+                    clinic.setBedNo(target.getBedNo());
+                    clinic.setWard(target.getWard());
+                    clinic.setWeight(String.valueOf(RandomUtil.randomInt(90,150)));
+                    clinic.setWeight(String.valueOf(RandomUtil.randomInt(150,200)));
+                    clinic.setAnaDoctor(getName());
+                    clinic.setAnaType(anaTypes.get(RandomUtil.randomInt(anaTypes.size()-1)));
+                    clinic.setAnalType(analTypes.get(RandomUtil.randomInt(analTypes.size()-1)));
+                    clinic.setSurgeryDoctor(getName());
+                    clinic.setConfigPerson(getName());
+                    clinic.setPatientCode(target.getPatientCode());
+                    clinic.setMonitorType(true);
+                    clinic.setTenantId("1");
+                    source.add(clinic);
+                }
+                equalsStrategyHandler.handle(source,targets);
+            });
+
+        });
+
+    }
+    public void fillPatientCode(){
+        List<BusPatientEntity> list = patientService.list();
+    }
+
+    @Test
+    public void manualSave(){
+        for (int i = 0; i < 30; i++) {
+            ClinicEditVo clinicEditVo = new ClinicEditVo();
+            BusClinicEntity clinic = new BusClinicEntity();
+            clinic.setStartTime(new Date());
+            clinic.setSurgeryName(clinicNames.get(RandomUtil.randomInt(clinicNames.size()-1)));
+            clinic.setPatientAge(RandomUtil.randomInt(15,50));
+            clinic.setPatientName(getName());
+            clinic.setPatientGender(EnumUtil.likeValueOf(SexEnum.class,RandomUtil.randomInt(1,2)));
+            clinic.setBedNo(String.valueOf(RandomUtil.randomInt(10,100)));
+            clinic.setWard(String.valueOf(RandomUtil.randomInt(10,100)));
+            clinic.setWeight(String.valueOf(RandomUtil.randomInt(90,150)));
+            clinic.setWeight(String.valueOf(RandomUtil.randomInt(150,200)));
+            clinic.setAnaDoctor(getName());
+            clinic.setAnaType(anaTypes.get(RandomUtil.randomInt(anaTypes.size()-1)));
+            clinic.setAnalType(analTypes.get(RandomUtil.randomInt(analTypes.size()-1)));
+            clinic.setSurgeryDoctor(getName());
+            clinic.setConfigPerson(getName());
+            clinic.setPatientCode(String.valueOf(RandomUtil.randomInt(100000,300000)));
+            clinic.setTenantId("1");
+
+            clinicEditVo.setClinic(clinic);
+
+            BusDeviceManualEntity manual = new BusDeviceManualEntity();
+            manual.setType(DeviceManualEnum.machine);
+            manual.setTotalDose(RandomUtil.randomInt(100));
+            manual.setContinueDose(RandomUtil.randomBigDecimal(BigDecimal.valueOf(10)));
+            manual.setSelfControlDose(RandomUtil.randomBigDecimal(BigDecimal.valueOf(10)));
+            manual.setSelfControlLockTime(RandomUtil.randomInt(100));
+            clinicEditVo.setManual(manual);
+//            manualController.save(clinicEditVo);
+
+//        patientService.save(patient);
+//        clinicService.save(clinic);
+        }
+    }
+
+    @Test
+    public void updateClinicPatientId(){
+        List<BusClinicEntity> list = clinicService.list();
+        for (BusClinicEntity clinicEntity : list) {
+            BusPatientEntity patientEntity = patientService.getOne(new QueryWrapper<BusPatientEntity>()
+                    .lambda().eq(BusPatientEntity::getCode, clinicEntity.getPatientCode())
+                    .eq(BusPatientEntity::getTenantId, clinicEntity.getTenantId()));
+            if(patientEntity==null){
+                continue;
+            }
+            clinicEntity.setPatientId(patientEntity.getId());
+        }
+        clinicService.updateBatchById(list);
+    }
+
+    @Test
+    public void updateInfusionPatientId(){
+        List<BusInfusionHistoryEntity> list = infusionHistoryService.list();
+        for (BusInfusionHistoryEntity infusionHistoryEntity : list) {
+            BusPatientEntity patientEntity = patientService.getOne(new QueryWrapper<BusPatientEntity>()
+                    .lambda().eq(BusPatientEntity::getCode, infusionHistoryEntity.getPatientCode())
+                    .eq(BusPatientEntity::getTenantId, infusionHistoryEntity.getTenantId()));
+            if(patientEntity==null){
+                continue;
+            }
+            infusionHistoryEntity.setPatientId(patientEntity.getId());
+        }
+        infusionHistoryService.updateBatchById(list);
+    }
+
+    public static void main(String[] args) {
+        System.out.println(getName());
+    }
+
+    public static String getName(){
+        Random random=new Random(System.currentTimeMillis());
+        /* 598 百家姓 */
+        String[] Surname= {"赵","钱","孙","李","周","吴","郑","王","冯","陈","褚","卫","蒋","沈","韩","杨","朱","秦","尤","许",
+                "何","吕","施","张","孔","曹","严","华","金","魏","陶","姜","戚","谢","邹","喻","柏","水","窦","章","云","苏","潘","葛","奚","范","彭","郎",
+                "鲁","韦","昌","马","苗","凤","花","方","俞","任","袁","柳","酆","鲍","史","唐","费","廉","岑","薛","雷","贺","倪","汤","滕","殷",
+                "罗","毕","郝","邬","安","常","乐","于","时","傅","皮","卞","齐","康","伍","余","元","卜","顾","孟","平","黄","和",
+                "穆","萧","尹","姚","邵","湛","汪","祁","毛","禹","狄","米","贝","明","臧","计","伏","成","戴","谈","宋","茅","庞","熊","纪","舒",
+                "屈","项","祝","董","梁","杜","阮","蓝","闵","席","季","麻","强","贾","路","娄","危","江","童","颜","郭","梅","盛","林","刁","钟",
+                "徐","邱","骆","高","夏","蔡","田","樊","胡","凌","霍","虞","万","支","柯","昝","管","卢","莫","经","房","裘","缪","干","解","应",
+                "宗","丁","宣","贲","邓","郁","单","杭","洪","包","诸","左","石","崔","吉","钮","龚","程","嵇","邢","滑","裴","陆","荣","翁","荀",
+                "羊","于","惠","甄","曲","家","封","芮","羿","储","靳","汲","邴","糜","松","井","段","富","巫","乌","焦","巴","弓","牧","隗","山",
+                "谷","车","侯","宓","蓬","全","郗","班","仰","秋","仲","伊","宫","宁","仇","栾","暴","甘","钭","厉","戎","祖","武","符","刘","景",
+                "詹","束","龙","叶","幸","司","韶","郜","黎","蓟","溥","印","宿","白","怀","蒲","邰","从","鄂","索","咸","籍","赖","卓","蔺","屠",
+                "蒙","池","乔","阴","郁","胥","能","苍","双","闻","莘","党","翟","谭","贡","劳","逄","姬","申","扶","堵","冉","宰","郦","雍","却",
+                "璩","桑","桂","濮","牛","寿","通","边","扈","燕","冀","浦","尚","农","温","别","庄","晏","柴","瞿","阎","充","慕","连","茹","习",
+                "宦","艾","鱼","容","向","古","易","慎","戈","廖","庾","终","暨","居","衡","步","都","耿","满","弘","匡","国","文","寇","广","禄",
+                "阙","东","欧","殳","沃","利","蔚","越","夔","隆","师","巩","厍","聂","晁","勾","敖","融","冷","訾","辛","阚","那","简","饶","空",
+                "曾","毋","沙","乜","养","鞠","须","丰","巢","关","蒯","相","查","后","荆","红","游","郏","竺","权","逯","盖","益","桓","公","仉",
+                "督","岳","帅","缑","亢","况","郈","有","琴","归","海","晋","楚","闫","法","汝","鄢","涂","钦","商","牟","佘","佴","伯","赏","墨",
+                "哈","谯","篁","年","爱","阳","佟","言","福","南","火","铁","迟","漆","官","冼","真","展","繁","檀","祭","密","敬","揭","舜","楼",
+                "疏","冒","浑","挚","胶","随","高","皋","原","种","练","弥","仓","眭","蹇","覃","阿","门","恽","来","綦","召","仪","风","介","巨",
+                "木","京","狐","郇","虎","枚","抗","达","杞","苌","折","麦","庆","过","竹","端","鲜","皇","亓","老","是","秘","畅","邝","还","宾",
+                "闾","辜","纵","侴","万俟","司马","上官","欧阳","夏侯","诸葛","闻人","东方","赫连","皇甫","羊舌","尉迟","公羊","澹台","公冶","宗正",
+                "濮阳","淳于","单于","太叔","申屠","公孙","仲孙","轩辕","令狐","钟离","宇文","长孙","慕容","鲜于","闾丘","司徒","司空","兀官","司寇",
+                "南门","呼延","子车","颛孙","端木","巫马","公西","漆雕","车正","壤驷","公良","拓跋","夹谷","宰父","谷梁","段干","百里","东郭","微生",
+                "梁丘","左丘","东门","西门","南宫","第五","公仪","公乘","太史","仲长","叔孙","屈突","尔朱","东乡","相里","胡母","司城","张廖","雍门",
+                "毋丘","贺兰","綦毋","屋庐","独孤","南郭","北宫","王孙"};
+
+        int index=random.nextInt(Surname.length-1);
+        String name = Surname[index]; //获得一个随机的姓氏
+
+        /* 从常用字中选取一个或两个字作为名 */
+        if(random.nextBoolean()){
+            name+=getChinese()+getChinese();
+        }else {
+            name+=getChinese();
+        }
+        return name;
+    }
+
+    public static String getChinese() {
+        String str = null;
+        int highPos, lowPos;
+        Random random = new Random();
+        highPos = (176 + Math.abs(random.nextInt(71)));//区码,0xA0打头,从第16区开始,即0xB0=11*16=176,16~55一级汉字,56~87二级汉字
+        random = new Random();
+        lowPos = 161 + Math.abs(random.nextInt(94));//位码,0xA0打头,范围第1~94列
+
+        byte[] bArr = new byte[2];
+        bArr[0] = (new Integer(highPos)).byteValue();
+        bArr[1] = (new Integer(lowPos)).byteValue();
+        try {
+            str = new String(bArr, "GB2312");    //区位码组合成汉字
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        return str;
+    }
+}

+ 28 - 0
nb-admin/src/test/java/com/nb/admin/FileUploadTest.java

@@ -0,0 +1,28 @@
+package com.nb.admin;
+
+import cn.hutool.core.io.FileUtil;
+import com.nb.common.util.MinioUtil;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * @Author longsanlang
+ * @Date 2022-04-06 17:55:44
+ * @Version 1.0
+ * @Description XXX
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AdminApplication.class)
+public class FileUploadTest {
+    @Autowired
+    MinioUtil minioUtil;
+
+    @Test
+    public void upload(){
+        String s = minioUtil.uploadObject(FileUtil.getInputStream(FileUtil.file("C:\\Users\\JR\\Desktop\\新疆患者数据.xls")), "/docker-compose.yml");
+        System.out.println(s);
+    }
+}

+ 118 - 0
nb-admin/src/test/java/com/nb/admin/HisStrategyTest.java

@@ -0,0 +1,118 @@
+package com.nb.admin;
+
+import cn.hutool.core.date.DateField;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.EnumUtil;
+import cn.hutool.core.util.RandomUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.nb.bus.entity.BusClinicEntity;
+import com.nb.bus.hospital.his.strategy.HisStrategyEnum;
+import com.nb.bus.hospital.his.strategy.HisStrategyHandler;
+import com.nb.bus.hospital.his.strategy.HisStrategyManager;
+import com.nb.bus.hospital.his.strategy.HisStrategyManagerRegister;
+import com.nb.bus.service.LocalBusClinicService;
+import com.nb.common.enums.SexEnum;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import static com.nb.admin.BusPatientTest.getName;
+
+/**
+ * @Author XX
+ * @Date 2022-04-25 19:00:10
+ * @Version 1.0
+ * @Description XXX
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AdminApplication.class)
+public class HisStrategyTest {
+
+    @Autowired
+    HisStrategyManagerRegister managerRegister;
+
+    @Autowired
+    LocalBusClinicService clinicService;
+
+    String patientCode="120263";
+    String tenantId="1";
+    @Test
+    public void allEquals(){
+        HisStrategyManager<? extends HisStrategyHandler> hisStrategyManager = managerRegister.get(HisStrategyEnum.ALL);
+
+    }
+
+
+    @Test
+    public void allMoreToLess(){
+        HisStrategyManager<? extends HisStrategyHandler> hisStrategyManager = managerRegister.get(HisStrategyEnum.ALL);
+        List<BusClinicEntity> sources = new ArrayList<>();
+        List<BusClinicEntity> targets = clinicService.list(new QueryWrapper<BusClinicEntity>().lambda().eq(BusClinicEntity::getPatientCode, patientCode).eq(BusClinicEntity::getTenantId, tenantId));
+        for (BusClinicEntity target : targets) {
+            BusClinicEntity clinic = new BusClinicEntity();
+            clinic.setStartTime(target.getStartTime()==null?
+                    RandomUtil.randomDate(DateUtil.beginOfMonth(new Date()), DateField.HOUR,-30,30):DateUtil.date(target.getStartTime()).offset(DateField.HOUR_OF_DAY,-1));
+            clinic.setSurgeryName(BusPatientTest.clinicNames.get(RandomUtil.randomInt(BusPatientTest.clinicNames.size()-1)));
+            clinic.setPatientName(getName());
+            clinic.setPatientGender(EnumUtil.likeValueOf(SexEnum.class,RandomUtil.randomInt(1,2)));
+            clinic.setBedNo(target.getBedNo());
+            clinic.setWard(target.getWard());
+            clinic.setWeight(String.valueOf(RandomUtil.randomInt(90,150)));
+            clinic.setWeight(String.valueOf(RandomUtil.randomInt(150,200)));
+            clinic.setAnaDoctor(getName());
+            clinic.setAnaType(BusPatientTest.anaTypes.get(RandomUtil.randomInt(BusPatientTest.anaTypes.size()-1)));
+            clinic.setAnalType(BusPatientTest.analTypes.get(RandomUtil.randomInt(BusPatientTest.analTypes.size()-1)));
+            clinic.setSurgeryDoctor(getName());
+            clinic.setConfigPerson(getName());
+            clinic.setPatientCode(target.getPatientCode());
+            clinic.setMonitorType(true);
+            clinic.setTenantId("1");
+            sources.add(clinic);
+        }
+        BusClinicEntity clinic = new BusClinicEntity();
+        clinic.setStartTime(DateUtil.beginOfDay(new Date()));
+        clinic.setSurgeryName(BusPatientTest.clinicNames.get(RandomUtil.randomInt(BusPatientTest.clinicNames.size()-1)));
+        clinic.setPatientName(getName());
+        clinic.setPatientGender(EnumUtil.likeValueOf(SexEnum.class,RandomUtil.randomInt(1,2)));
+        clinic.setBedNo("10");
+        clinic.setWard("10");
+        clinic.setWeight(String.valueOf(RandomUtil.randomInt(90,150)));
+        clinic.setWeight(String.valueOf(RandomUtil.randomInt(150,200)));
+        clinic.setAnaDoctor(getName());
+        clinic.setAnaType(BusPatientTest.anaTypes.get(RandomUtil.randomInt(BusPatientTest.anaTypes.size()-1)));
+        clinic.setAnalType(BusPatientTest.analTypes.get(RandomUtil.randomInt(BusPatientTest.analTypes.size()-1)));
+        clinic.setSurgeryDoctor(getName());
+        clinic.setConfigPerson(getName());
+        clinic.setPatientCode(patientCode);
+        clinic.setMonitorType(true);
+        clinic.setTenantId("1");
+        sources.add(clinic);
+        hisStrategyManager.getHandlers()
+                .stream()
+                .filter(handler-> handler.apply(sources,targets))
+                .forEach(handler->handler.handle(sources,targets));
+    }
+
+
+    @Test
+    public void allLessToMore(){
+        HisStrategyManager<? extends HisStrategyHandler> hisStrategyManager = managerRegister.get(HisStrategyEnum.ALL);
+
+    }
+    @Test
+    public void onlyNew(){
+        HisStrategyManager<? extends HisStrategyHandler> hisStrategyManager = managerRegister.get(HisStrategyEnum.NEW);
+
+    }
+
+    @Test
+    public void part(){
+        HisStrategyManager<? extends HisStrategyHandler> hisStrategyManager = managerRegister.get(HisStrategyEnum.PART);
+    }
+}

+ 20 - 0
nb-admin/src/test/java/com/nb/admin/SpringBootApplicationTests.java

@@ -0,0 +1,20 @@
+package com.nb.admin;
+
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName SpringBootApplicationTests.java
+ * @Description TODO
+ * @createTime 2022年03月17日 13:50:00
+ */
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = AdminApplication.class)
+public class SpringBootApplicationTests {
+
+
+
+}

+ 28 - 0
nb-common/src/main/java/com/nb/common/enums/LinkExternalEnum.java

@@ -0,0 +1,28 @@
+package com.nb.common.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+
+/**
+ * 是否外链
+ *
+ * @author Kevin
+ */
+@Getter
+@AllArgsConstructor
+public enum LinkExternalEnum {
+
+    /**
+     * 是
+     */
+    YES("0", "是"),
+    /**
+     * 否
+     */
+    NO("1", "否");
+
+    private String code;
+    private String desc;
+
+}

+ 59 - 0
nb-framework/src/main/java/com/nb/framework/aspect/DataSourceAspect.java

@@ -0,0 +1,59 @@
+package com.nb.framework.aspect;
+
+import com.nb.common.annotation.DataSource;
+import com.nb.framework.datasource.DynamicDataSourceContextHolder;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+/**
+ * 多数据源处理
+ *
+ * @author Kevin
+ */
+@Aspect
+@Order(1)
+@Component
+@Slf4j
+public class DataSourceAspect {
+
+    @Pointcut("@annotation(com.nb.common.annotation.DataSource)"
+            + "|| @within(com.nb.common.annotation.DataSource)")
+    public void dsPointCut() {
+
+    }
+
+    @Around("dsPointCut()")
+    public Object around(ProceedingJoinPoint point) throws Throwable {
+        DataSource dataSource = getDataSource(point);
+        if (Objects.nonNull(dataSource)) {
+            DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
+        }
+        try {
+            return point.proceed();
+        } finally {
+            // 销毁数据源 在执行方法之后
+            DynamicDataSourceContextHolder.clearDataSourceType();
+        }
+    }
+
+    /**
+     * 获取需要切换的数据源
+     */
+    public DataSource getDataSource(ProceedingJoinPoint point) {
+        MethodSignature signature = (MethodSignature) point.getSignature();
+        DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);
+        if (Objects.nonNull(dataSource)) {
+            return dataSource;
+        }
+        return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class);
+    }
+}

+ 39 - 0
nb-framework/src/main/java/com/nb/framework/aspect/DemoModelAspect.java

@@ -0,0 +1,39 @@
+package com.nb.framework.aspect;
+
+import com.nb.common.config.AppConfig;
+import com.nb.common.exception.DemoModeException;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 演示环境AOP
+ *
+ * @author Kevin
+ */
+@Aspect
+@Component
+@Slf4j
+public class DemoModelAspect {
+
+    @Pointcut("execution(public * com.nb..*.controller..*(..))")
+    public void pointcut() {
+    }
+
+    @Before("pointcut()")
+    public void before() {
+        HttpServletRequest requset = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+        if (AppConfig.isDemoEnabled()
+                && "POST".equals(requset.getMethod())
+                && !requset.getRequestURI().contains("/login")) {
+            throw new DemoModeException();
+        }
+    }
+
+}

+ 146 - 0
nb-framework/src/main/java/com/nb/framework/aspect/LogAspect.java

@@ -0,0 +1,146 @@
+package com.nb.framework.aspect;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.thread.ThreadUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import cn.hutool.http.useragent.UserAgent;
+import cn.hutool.http.useragent.UserAgentUtil;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.nb.common.annotation.Log;
+import com.nb.common.enums.LogStatusEnum;
+import com.nb.common.util.AddressUtil;
+import com.nb.common.util.IpUtil;
+import com.nb.common.util.SecurityUtil;
+import com.nb.system.entity.SysLog;
+import com.nb.system.service.ISysLogService;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 日志注解
+ *
+ * @author Kevin
+ */
+@Aspect
+@Component
+@Slf4j
+public class LogAspect {
+
+    @Pointcut("@annotation(com.nb.common.annotation.Log)")
+    public void logPointCut() {
+    }
+
+    @Around("logPointCut()")
+    @SneakyThrows
+    public Object around(ProceedingJoinPoint point) {
+        Long startTime = System.currentTimeMillis();
+        Object obj = null;
+        Exception exception = null;
+        try {
+            obj = point.proceed();
+        } catch (Exception e) {
+            exception = e;
+            throw e;
+        } finally {
+            Long endTime = System.currentTimeMillis();
+            handleLog(point, exception, obj, endTime - startTime);
+        }
+        return obj;
+    }
+
+    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object retValue, long time) {
+        try {
+            // 获得注解
+            Signature signature = joinPoint.getSignature();
+            MethodSignature methodSignature = (MethodSignature) signature;
+            Log controllerLog = AnnotationUtils.findAnnotation(methodSignature.getMethod(), Log.class);
+            if (Objects.isNull(controllerLog)) {
+                return;
+            }
+            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+            SysLog sysLog = new SysLog();
+            sysLog.setTitle(controllerLog.title());
+            sysLog.setLogStatus(LogStatusEnum.SUCCESS.getCode());
+            sysLog.setUserPlatform(controllerLog.userPlatform().getCode());
+            sysLog.setRequsetUri(request.getRequestURI());
+            sysLog.setRequsetType(request.getMethod());
+            // 设置方法名称
+            String className = joinPoint.getTarget().getClass().getName();
+            String methodName = joinPoint.getSignature().getName();
+            sysLog.setRequsetMethod(className + "." + methodName + "()");
+
+            List<Object> objectList = Arrays.stream(joinPoint.getArgs()).filter(item -> Objects.nonNull(item) && !isFilterObject(item)).collect(Collectors.toList());
+            sysLog.setRequsetParams(JSONUtil.toJsonStr(objectList));
+            sysLog.setResponseResult(JSONUtil.toJsonStr(retValue));
+            sysLog.setRequsetTime(String.valueOf(time));
+
+            String ipAddress = IpUtil.getClientIp(request);
+            sysLog.setIpAddress(ipAddress);
+            sysLog.setOperLocation(AddressUtil.getRealAddressByIp(ipAddress));
+            UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
+            sysLog.setBrowser(userAgent.getBrowser().getName());
+            sysLog.setOs(userAgent.getOs().getName());
+
+            if (StpUtil.isLogin()) {
+                sysLog.setOperName(SecurityUtil.getUsername());
+            }
+            if (e != null) {
+                sysLog.setLogStatus(LogStatusEnum.FAILURE.getCode());
+                sysLog.setException(e.getMessage());
+            }
+            ThreadUtil.execAsync(() -> {
+                ISysLogService sysLogService = SpringUtil.getBean(ISysLogService.class);
+                sysLogService.save(sysLog);
+            });
+            log.debug("接口:{},URI:{},执行耗时:{}", sysLog.getTitle(), sysLog.getRequsetUri(), time >= 1000 ? time / 1000 + "s" : time + "ms");
+        } catch (Exception exception) {
+            log.error("异常信息:{}", exception.getMessage());
+            exception.printStackTrace();
+        }
+    }
+
+    /**
+     * 判断是否需要过滤的对象。
+     *
+     * @param o 对象信息。
+     * @return 如果是需要过滤的对象,则返回true;否则返回false。
+     */
+    public boolean isFilterObject(final Object o) {
+        Class<?> clazz = o.getClass();
+        if (clazz.isArray()) {
+            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
+        } else if (Collection.class.isAssignableFrom(clazz)) {
+            Collection collection = (Collection) o;
+            for (Object value : collection) {
+                return value instanceof MultipartFile;
+            }
+        } else if (Map.class.isAssignableFrom(clazz)) {
+            Map map = (Map) o;
+            for (Object value : map.entrySet()) {
+                Map.Entry entry = (Map.Entry) value;
+                return entry.getValue() instanceof MultipartFile;
+            }
+        }
+        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
+                || o instanceof BindingResult || o instanceof Page;
+    }
+}

+ 52 - 0
nb-framework/src/main/java/com/nb/framework/config/ApplicationConfig.java

@@ -0,0 +1,52 @@
+package com.nb.framework.config;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializerProvider;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+
+import java.io.IOException;
+import java.util.TimeZone;
+
+/**
+ * 开启AOP代理自动配置
+ *
+ * @author Kevin
+ */
+@Configuration
+@EnableAspectJAutoProxy(exposeProxy = true)
+public class ApplicationConfig {
+    /**
+     * 时区配置
+     */
+    @Bean
+    public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() {
+        return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault());
+    }
+
+    /**
+     * null序列化成空字符串
+     *
+     * @param objectMapper
+     * @return
+     */
+    @Bean
+    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
+        objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
+            @Override
+            public void serialize(Object param, JsonGenerator jsonGenerator,
+                                  SerializerProvider paramSerializerProvider) throws IOException {
+                jsonGenerator.writeString("");
+            }
+        });
+        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
+        mappingJackson2HttpMessageConverter.setObjectMapper(objectMapper);
+        return mappingJackson2HttpMessageConverter;
+    }
+
+}

+ 108 - 0
nb-framework/src/main/java/com/nb/framework/config/DruidConfig.java

@@ -0,0 +1,108 @@
+package com.nb.framework.config;
+
+import cn.hutool.extra.spring.SpringUtil;
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
+import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
+import com.alibaba.druid.util.Utils;
+import com.nb.common.enums.DataSourceTypeEnum;
+import com.nb.framework.config.properties.DruidProperties;
+import com.nb.framework.datasource.DynamicDataSource;
+import com.google.common.collect.Maps;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+
+import javax.servlet.*;
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * druid 配置多数据源
+ *
+ * @author Kevin
+ */
+@Configuration
+public class DruidConfig {
+    @Bean
+    @ConfigurationProperties("spring.datasource.druid.master")
+    public DataSource masterDataSource(DruidProperties druidProperties) {
+        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
+        return druidProperties.dataSource(dataSource);
+    }
+
+    @Bean
+    @ConfigurationProperties("spring.datasource.druid.slave")
+    @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
+    public DataSource slaveDataSource(DruidProperties druidProperties) {
+        DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
+        return druidProperties.dataSource(dataSource);
+    }
+
+    @Bean(name = "dynamicDataSource")
+    @Primary
+    public DynamicDataSource dataSource(DataSource masterDataSource) {
+        Map<Object, Object> targetDataSources = Maps.newHashMap();
+        targetDataSources.put(DataSourceTypeEnum.MASTER.name(), masterDataSource);
+        setDataSource(targetDataSources, DataSourceTypeEnum.SLAVE.name(), "slaveDataSource");
+        return new DynamicDataSource(masterDataSource, targetDataSources);
+    }
+
+    /**
+     * 设置数据源
+     *
+     * @param targetDataSources 备选数据源集合
+     * @param sourceName        数据源名称
+     * @param beanName          bean名称
+     */
+    public void setDataSource(Map<Object, Object> targetDataSources, String sourceName, String beanName) {
+        try {
+            DataSource dataSource = SpringUtil.getBean(beanName);
+            targetDataSources.put(sourceName, dataSource);
+        } catch (Exception e) {
+        }
+    }
+
+    /**
+     * 去除监控页面底部的广告
+     */
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    @Bean
+    @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true")
+    public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) {
+        // 获取web监控页面的参数
+        DruidStatProperties.StatViewServlet config = properties.getStatViewServlet();
+        // 提取common.js的配置路径
+        String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*";
+        String commonJsPattern = pattern.replaceAll("\\*", "js/common.js");
+        final String filePath = "support/http/resources/js/common.js";
+        // 创建filter进行过滤
+        Filter filter = new Filter() {
+            @Override
+            public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
+            }
+
+            @Override
+            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+                chain.doFilter(request, response);
+                String text = Utils.readFromResource(filePath);
+                /** 正则替换banner, 除去底部的广告信息 **/
+                text = text.replaceAll("<a.*?banner\"></a><br/>", "");
+                text = text.replaceAll("powered.*?shrek.wang</a>", "");
+                response.getWriter().write(text);
+            }
+
+            @Override
+            public void destroy() {
+            }
+        };
+        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
+        registrationBean.setFilter(filter);
+        registrationBean.addUrlPatterns(commonJsPattern);
+        return registrationBean;
+    }
+}

+ 75 - 0
nb-framework/src/main/java/com/nb/framework/config/RedissonClientAutoConfiguration.java

@@ -0,0 +1,75 @@
+//
+// Source code recreated from a .class file by IntelliJ IDEA
+// (powered by Fernflower decompiler)
+//
+
+package com.nb.framework.config;
+
+import org.redisson.Redisson;
+import org.redisson.api.RedissonClient;
+import org.redisson.config.*;
+import org.redisson.spring.starter.RedissonProperties;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
+import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.core.RedisOperations;
+import java.time.Duration;
+import java.util.*;
+
+@Configuration
+@ConditionalOnClass({Redisson.class, RedisOperations.class})
+@AutoConfigureBefore({RedisAutoConfiguration.class})
+@EnableConfigurationProperties({RedissonProperties.class, RedisProperties.class})
+public class RedissonClientAutoConfiguration {
+
+    @Autowired
+    private RedisProperties redisProperties;
+
+    public RedissonClientAutoConfiguration() {
+    }
+
+    @Bean(
+        destroyMethod = "shutdown"
+    )
+    @ConditionalOnMissingBean({RedissonClient.class})
+    public RedissonClient redisson() {
+        Config config = new Config();
+        SingleServerConfig singleServerConfig= config.useSingleServer();
+        singleServerConfig.setDatabase(redisProperties.getDatabase());
+        singleServerConfig.setPassword(redisProperties.getPassword());
+        singleServerConfig.setAddress("redis://"+redisProperties.getHost()+":"+redisProperties.getPort());
+        singleServerConfig.setPingConnectionInterval(0);
+        //  如果当前连接池里的连接数量超过了最小空闲连接数,而同时有连接空闲时间超过了该数值,那么这些连接将会自动被关闭,并从连接池里去掉。时间单位是毫秒。默认10000
+        singleServerConfig.setIdleConnectionTimeout(30000);
+        //建立连接时的等待超时。时间单位是毫秒。默认10000
+        singleServerConfig.setConnectTimeout(Long.valueOf(Optional.ofNullable(redisProperties.getConnectTimeout()).orElse(Duration.ofSeconds(3)).toMillis()).intValue());
+        //等待节点回复命令的时间。该时间从命令发送成功时开始计时。默认3000
+        singleServerConfig.setTimeout(Long.valueOf(Optional.ofNullable(redisProperties.getTimeout()).orElse(Duration.ofSeconds(3)).toMillis()).intValue());
+        //命令失败重试次数,如果尝试达到 retryAttempts(命令失败重试次数) 仍然不能将命令发送至某个指定的节点时,将抛出错误,如果尝试在此限制之内发送成功,则开始启用 timeout(命令等待超时) 计时。
+        singleServerConfig.setRetryAttempts(3);
+        //在一条命令发送失败以后,等待重试发送的时间间隔。时间单位是毫秒。默认1500
+        singleServerConfig.setRetryInterval(1500);
+        //每个连接的最大订阅数量。默认5
+        singleServerConfig.setSubscriptionsPerConnection(5000);
+        //在Redis节点里显示的客户端名称。默认null
+        singleServerConfig.setClientName("nb");
+        //用于发布和订阅连接的最小保持连接数(长连接)。Redisson内部经常通过发布和订阅来实现许多功能,长期保持一定数量的发布订阅连接是必须的。默认1
+        singleServerConfig.setSubscriptionConnectionMinimumIdleSize(1);
+        //多从节点的环境里,每个从服务节点里用于发布和订阅连接的连接池最大容量。连接池的连接数量自动弹性伸缩。默认50
+        singleServerConfig.setSubscriptionConnectionPoolSize(500);
+        //是否保持连接
+        singleServerConfig.setKeepAlive(false);
+        //这个线程池数量被所有RTopic对象监听器,RRemoteService调用者和RExecutorService任务共同共享。默认当前处理核数量 * 2
+        config.setThreads(Runtime.getRuntime().availableProcessors()*2);
+        // 这个线程池数量是在一个Redisson实例内,被其创建的所有分布式数据类型和服务,以及底层客户端所一同共享的线程池里保存的线程数量。
+        config.setThreads(Runtime.getRuntime().availableProcessors()*2);
+        config.setTransportMode(TransportMode.NIO);
+        return Redisson.create(config);
+    }
+}

+ 25 - 0
nb-framework/src/main/java/com/nb/framework/config/SaConfig.java

@@ -0,0 +1,25 @@
+package com.nb.framework.config;
+
+import cn.dev33.satoken.SaManager;
+import cn.dev33.satoken.dao.SaTokenDaoRedisJackson;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName SaConfig.java
+ * @Description TODO
+ * @createTime 2022年03月15日 16:51:00
+ */
+@Component
+@AllArgsConstructor
+public class SaConfig {
+    private final SaTokenDaoRedisJackson saTokenDaoRedisJackson;
+    @PostConstruct
+    public void init(){
+        SaManager.setSaTokenDao(saTokenDaoRedisJackson);
+    }
+}

+ 55 - 0
nb-framework/src/main/java/com/nb/framework/config/SaTokenConfig.java

@@ -0,0 +1,55 @@
+package com.nb.framework.config;
+
+import cn.dev33.satoken.interceptor.SaRouteInterceptor;
+import cn.dev33.satoken.router.SaRouter;
+import cn.dev33.satoken.stp.StpUtil;
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.web.servlet.HandlerInterceptor;
+import java.util.List;
+
+/**
+ * Sa Token 注册拦截器
+ *
+ * @author Kevin
+ */
+@Slf4j
+@Configuration
+public class SaTokenConfig {
+
+    @Bean
+    @Order(1)
+    public HandlerInterceptor handlerInterceptor(){
+        return new SaRouteInterceptor((req, res, handler) -> {
+            SaRouter.match("/**")
+                    .notMatch(IGNORE_URL)
+                    .check(StpUtil::checkLogin);
+        });
+    }
+    private static final List<String> IGNORE_URL = Lists.newArrayList();
+
+    static {
+        IGNORE_URL.add("/api/favicon.ico");
+        IGNORE_URL.add("/sys/app/get");
+        IGNORE_URL.add("/index");
+        IGNORE_URL.add("/login");
+        IGNORE_URL.add("/logout");
+        IGNORE_URL.add("/common/**");
+        IGNORE_URL.add("/*.html");
+        IGNORE_URL.add("/**/*.html");
+        IGNORE_URL.add("/error");
+        IGNORE_URL.add("/swagger-resources/**");
+        IGNORE_URL.add("/swagger-ui/**");
+        IGNORE_URL.add("/webjars/**");
+        IGNORE_URL.add("/v2/api-docs/*");
+        IGNORE_URL.add("/v2/api-docs");
+        IGNORE_URL.add("/authority/captcha/**");
+        IGNORE_URL.add("/system/curl/**");
+        IGNORE_URL.add("/system/sysConfig/getTime");
+        IGNORE_URL.add("/system/sysDept/**");
+    }
+
+}

+ 128 - 0
nb-framework/src/main/java/com/nb/framework/config/SwaggerConfig.java

@@ -0,0 +1,128 @@
+package com.nb.framework.config;
+
+
+import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
+import io.swagger.models.auth.In;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.ParameterBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.schema.ModelRef;
+import springfox.documentation.service.*;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
+
+import java.util.*;
+
+@Configuration
+@EnableKnife4j
+@EnableSwagger2WebMvc
+@Profile("dev")
+public class SwaggerConfig implements WebMvcConfigurer {
+
+    public static final String VERSION = "1.0.0";
+
+    ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("基于NB的网络泵物联网接入业务处理系统")
+                .description("基于NB的网络泵物联网接入业务处理系统")
+                .license("Apache 2.0")
+                .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
+                .termsOfServiceUrl("")
+                .version(VERSION)
+                .contact(new Contact("驼人医疗器械云智能部门","http://www.tuoren.com/", "miaorf@outlook.com"))
+                .build();
+    }
+
+    @Bean
+    public Docket system(){
+        return new Docket(DocumentationType.SWAGGER_2)
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.nb.system.controller"))
+                .paths(PathSelectors.any())
+                .build()
+                .groupName("系统模块")
+                .apiInfo(apiInfo())
+                .securitySchemes(security())
+                .securityContexts(securityContexts())
+                .enable(true);
+    }
+
+    @Bean
+    public Docket bus(){
+        return new Docket(DocumentationType.SWAGGER_2)
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.nb.bus.controller"))
+                .paths(PathSelectors.any())
+                .build()
+                .groupName("业务模块")
+                .apiInfo(apiInfo())
+                .securitySchemes(security())
+                .securityContexts(securityContexts())
+                .enable(true);
+    }
+
+
+    @Bean
+    public Docket admin(){
+        ParameterBuilder ticketPar = new ParameterBuilder();
+        List<Parameter> pars = new ArrayList<Parameter>();
+        ticketPar.name("tenantId").description("租户id")
+                .modelRef(new ModelRef("string")).parameterType("query")
+                .required(true).build();
+        pars.add(ticketPar.build());
+
+        return new Docket(DocumentationType.SWAGGER_2)
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.nb.admin.controller"))
+                .paths(PathSelectors.any())
+                .build()
+                .groupName("登录模块")
+                .apiInfo(apiInfo())
+                .globalOperationParameters(pars)
+                .securitySchemes(security())
+                .securityContexts(securityContexts())
+                .enable(true);
+    }
+
+    private List<SecurityScheme> security() {
+        return Arrays.asList(new ApiKey("BASE_TOKEN", "Authorization",   In.HEADER.toValue()),
+                new ApiKey("租户id", "Tenant-Id",   In.HEADER.toValue()),
+                new ApiKey("签名", "Sign",   In.HEADER.toValue()),
+                new ApiKey("时间戳", "Timestamp",   In.HEADER.toValue()));
+    }
+
+    /**
+     * 授权信息全局应用
+     */
+    private List<SecurityContext> securityContexts() {
+        return Collections.singletonList(
+                SecurityContext.builder()
+                        .securityReferences(Collections.singletonList(new SecurityReference("BASE_TOKEN", new AuthorizationScope[]{new AuthorizationScope("global", "")})))
+                        .build()
+        );
+    }
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.
+                addResourceHandler( "/swagger-ui/**")
+                .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/")
+                .resourceChain(false);
+    }
+
+    @Override
+    public void addViewControllers(ViewControllerRegistry registry) {
+        registry.addViewController( "/swagger-ui/")
+                .setViewName("forward:" +  "/swagger-ui/index.html");
+    }
+
+}

+ 34 - 0
nb-framework/src/main/java/com/nb/framework/config/ValidatorConfig.java

@@ -0,0 +1,34 @@
+package com.nb.framework.config;
+
+import org.hibernate.validator.HibernateValidator;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+/**
+ * 校验框架配置类
+ *
+ * @author Kevin
+ */
+@Configuration
+public class ValidatorConfig {
+
+    /**
+     * 配置校验框架 快速返回模式
+     * <p>
+     * Spring Validation默认会校验完所有字段,然后才抛出异常。
+     * 可以通过一些简单的配置,开启Fali Fast模式,一旦校验失败就立即返回。
+     */
+    @Bean
+    public Validator validator() {
+        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
+                .configure()
+                .failFast(true)
+                .buildValidatorFactory();
+        return validatorFactory.getValidator();
+    }
+
+}

+ 120 - 0
nb-framework/src/main/java/com/nb/framework/config/WebAppMvcConfig.java

@@ -0,0 +1,120 @@
+package com.nb.framework.config;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import com.nb.common.config.BooleanToIntegerSerializer;
+import com.nb.framework.config.convert.EnumDeserializer;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.*;
+import com.fasterxml.jackson.databind.deser.std.StdScalarDeserializer;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+import com.fasterxml.jackson.databind.ser.std.StringSerializer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * @Description TODO
+ * @Classname Knife4jWebMvcConfig
+ * @Date 2021/3/2 15:27
+ * @Created by jianxiapc
+ */
+@Configuration
+@EnableAutoConfiguration
+//@Profile("dev")
+public class WebAppMvcConfig implements WebMvcConfigurer {
+    @Autowired
+    private ObjectMapper objectMapper;
+    @Autowired
+    private List<HandlerInterceptor> interceptors;
+    @Override
+    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
+        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
+        // 时间格式化
+        SerializerProvider serializerProvider = objectMapper.getSerializerProvider();
+        serializerProvider.setNullValueSerializer(new JsonSerializer<Object>() {
+            @Override
+            public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+                gen.writeNull();
+            }
+        });
+        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
+
+        SimpleModule booleanSimpleModule = new SimpleModule();
+        booleanSimpleModule.addSerializer(Boolean.class, new BooleanToIntegerSerializer());
+        booleanSimpleModule.addDeserializer(Boolean.class, new StdScalarDeserializer<Boolean>(Object.class) {
+            @Override
+            public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+                String text = p.getText();
+                if (StrUtil.isBlank(text)) {
+                    return null;
+                }
+                String lowerCase = text.toLowerCase();
+                if("true".equals(lowerCase)||"1".equals(lowerCase)){
+                    return Boolean.TRUE;
+                }else if("false".equals(lowerCase)||"0".equals(lowerCase)){
+                    return Boolean.FALSE;
+                }
+                return null;
+            }
+        });
+
+        SimpleModule stringModule = new SimpleModule();
+        stringModule.addSerializer(String.class,new StringSerializer());
+        stringModule.addDeserializer(String.class, new StdScalarDeserializer<String>(String.class) {
+            @Override
+            public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
+                String text = p.getText();
+                return StrUtil.isBlankIfStr(text)?null:text;
+            }
+        });
+        SimpleModule enumModule = new SimpleModule();
+        enumModule.addDeserializer(Enum.class, new EnumDeserializer());
+
+        objectMapper.registerModule(enumModule);
+        objectMapper.registerModule(stringModule);
+        objectMapper.registerModule(booleanSimpleModule);
+        // 设置格式化内容
+        converter.setObjectMapper(objectMapper);
+        converters.add(0, converter);
+    }
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+
+        registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
+
+        /** 配置knife4j 显示文档 */
+        registry.addResourceHandler("doc.html")
+                .addResourceLocations("classpath:/META-INF/resources/");
+
+        /**
+         * 配置swagger-ui显示文档
+         */
+        registry.addResourceHandler("swagger-ui.html")
+                .addResourceLocations("classpath:/META-INF/resources/");
+        /** 公共部分内容 */
+        registry.addResourceHandler("/webjars/**")
+                .addResourceLocations("classpath:/META-INF/resources/webjars/");
+    }
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        if(CollectionUtil.isNotEmpty(interceptors)){
+            interceptors.forEach(registry::addInterceptor);
+        }
+    }
+}

+ 33 - 0
nb-framework/src/main/java/com/nb/framework/config/aspect/PermissionAutoConfiguration.java

@@ -0,0 +1,33 @@
+package com.nb.framework.config.aspect;
+
+
+import org.springframework.aop.aspectj.AspectJExpressionPointcut;
+import org.springframework.aop.support.DefaultPointcutAdvisor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Component;
+
+/**
+ * ClassName:
+ * date: 2020/5/21
+ *
+ * @author lifang
+ * @version 1.0
+ */
+@Component
+public class PermissionAutoConfiguration {
+    private final String POINT_CUT= "@annotation(cn.dev33.satoken.annotation.SaCheckPermission)";
+    @Bean
+    public DefaultPointcutAdvisor permissionPointCutAdvice(){
+        //声明一个AspectJ切点
+        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
+        //设置切点表达式
+        pointcut.setExpression(POINT_CUT);
+        // 配置增强类advisor, 切面=切点+增强
+        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
+        //设置切点
+        advisor.setPointcut(pointcut);
+        //设置增强(Advice)
+        advisor.setAdvice(new PermissionMethodInterceptor());
+        return advisor;
+    }
+}

+ 45 - 0
nb-framework/src/main/java/com/nb/framework/config/aspect/PermissionMethodInterceptor.java

@@ -0,0 +1,45 @@
+package com.nb.framework.config.aspect;
+
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.annotation.SaMode;
+import cn.dev33.satoken.spring.SpringMVCUtil;
+import cn.dev33.satoken.stp.StpUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.annotation.Annotation;
+import java.util.List;
+
+@Slf4j
+public class PermissionMethodInterceptor implements MethodInterceptor {
+    @Override
+    public Object invoke(MethodInvocation invocation) throws Throwable {
+        Annotation[] annotations = invocation.getMethod().getAnnotations();
+        SaCheckPermission checkPermission=null;
+        for (Annotation annotation : annotations) {
+            if(annotation.annotationType().equals(SaCheckPermission.class)){
+                checkPermission= (SaCheckPermission) annotation;
+                break;
+            }
+        }
+        if(checkPermission==null){
+            return invocation.proceed();
+        }
+        HttpServletRequest request = SpringMVCUtil.getRequest();
+        //判断是否为系统级别用户,系统级别用户在所有医院为通用权限
+        List<String> permissionList = StpUtil.getPermissionList();
+        if (permissionList.contains("admin")) {
+            return invocation.proceed();
+        }else {
+            if(SaMode.AND.equals(checkPermission.mode())){
+                StpUtil.checkPermissionAnd(checkPermission.value());
+            }else if(SaMode.OR.equals(checkPermission.mode())){
+                StpUtil.checkPermissionOr(checkPermission.value());
+            }
+        }
+        return invocation.proceed();
+    }
+}

+ 43 - 0
nb-framework/src/main/java/com/nb/framework/config/convert/EnumConvertFactory.java

@@ -0,0 +1,43 @@
+package com.nb.framework.config.convert;
+
+import com.baomidou.mybatisplus.annotation.IEnum;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.core.convert.converter.ConverterFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+@Component
+public class EnumConvertFactory implements ConverterFactory<String, IEnum<?>> {
+
+    @Override
+    public <T extends IEnum<?>> Converter<String, T> getConverter(Class<T> targetType) {
+        return new StringToEnum<>(targetType);
+    }
+
+
+    public static class StringToEnum<T extends IEnum<?>> implements Converter<String, T> {
+
+        private final Class<T> targetType;
+
+        public StringToEnum(Class<T> targetType) {
+            this.targetType = targetType;
+        }
+
+        @Override
+        public T convert(String source) {
+            if (!StringUtils.hasText(source)) {
+                return null;
+            }
+            return (T) EnumConvertFactory.getEnum(this.targetType, source);
+        }
+    }
+
+    public static <T extends IEnum<?>> T getEnum(Class<T> targetType, String source) {
+        for (T constant : targetType.getEnumConstants()) {
+            if (source.equals(String.valueOf(constant.getValue()))) {
+                return constant;
+            }
+        }
+        return null;
+    }
+}

+ 62 - 0
nb-framework/src/main/java/com/nb/framework/config/convert/EnumDeserializer.java

@@ -0,0 +1,62 @@
+package com.nb.framework.config.convert;
+
+import com.baomidou.mybatisplus.annotation.IEnum;
+import com.nb.common.cache.value.Value;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.BeanProperty;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springframework.util.StringUtils;
+
+import java.io.IOException;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class EnumDeserializer extends JsonDeserializer<Enum<?>> implements ContextualDeserializer {
+
+    private Class<?> target;
+
+    @SuppressWarnings("all")
+    @Override
+    public Enum<?> deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
+        if (!StringUtils.hasText(jsonParser.getText())) {
+            return null;
+        }
+        if (IEnum.class.isAssignableFrom(target)) {
+            IEnum anEnum = EnumConvertFactory.getEnum((Class) target, jsonParser.getText());
+            if(anEnum!=null){
+                return (Enum<?>) anEnum;
+            }
+        }
+       return (Enum<?>) Value.simple(jsonParser.getText()).as(target);
+    }
+
+    /**
+     * @param ctx      ctx
+     * @param property property
+     * @return 1
+     * @throws JsonMappingException
+     */
+    @Override
+    public JsonDeserializer<?> createContextual(DeserializationContext ctx, BeanProperty property) throws JsonMappingException {
+        Class<?> rawCls = ctx.getContextualType().getRawClass();
+        EnumDeserializer enumDeserializer = new EnumDeserializer();
+        enumDeserializer.setTarget(rawCls);
+        return enumDeserializer;
+    }
+
+
+    public static Enum<?> defaultEnumTransform(Class<?> type, String indexString) {
+        Enum<?>[] enumConstants = (Enum<?>[]) type.getEnumConstants();
+        try {
+            int index = Integer.parseInt(indexString);
+            return enumConstants[index];
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+}

+ 186 - 0
nb-framework/src/main/java/com/nb/framework/config/mybatisplus/MybatisPlusConfig.java

@@ -0,0 +1,186 @@
+package com.nb.framework.config.mybatisplus;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ClassUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
+import com.baomidou.mybatisplus.extension.plugins.inner.*;
+import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.MySqlDialect;
+import com.nb.common.bo.LoginUser;
+import com.nb.common.entity.TenantGenericEntity;
+import com.nb.common.util.SecurityUtil;
+import com.nb.framework.config.mybatisplus.handler.CreateAndUpdateMetaObjectHandler;
+import com.nb.framework.config.mybatisplus.handler.CustomDataPermissionHandler;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.StringValue;
+import net.sf.jsqlparser.schema.Column;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.mock.web.MockHttpServletRequest;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST;
+
+/**
+ * mybatis-plus配置类
+ *
+ * @author Kevin
+ */
+@EnableTransactionManagement(proxyTargetClass = true)
+@Configuration
+@MapperScan({"${mybatis-plus.mapperPackage}","com.nb.framework.test.mapper"})
+public class MybatisPlusConfig {
+
+    private final TenantIdManager tenantIdManager;
+
+    private final List<Class<?>> tableClass;
+
+    private Set<String> ignoreTableName;
+    @Autowired
+    public MybatisPlusConfig(TenantIdManager tenantIdManager) {
+        this.tenantIdManager=tenantIdManager;
+        Set<Class<?>> classes = ClassUtil.scanPackage("com.coffee");
+        //找到所有@TableName修饰的类
+        tableClass = classes.stream()
+                .filter(aClass -> ObjectUtil.isNotNull(aClass.getAnnotation(TableName.class)))
+                .collect(Collectors.toList());
+        if (CollectionUtil.isNotEmpty(tableClass)) {
+            ignoreTableName =
+                    tableClass
+                            .stream()
+                            .filter(aClass -> !ClassUtil.isAssignable(TenantGenericEntity.class,aClass) )
+                            .map(aClass -> {
+                                TableName tableName = aClass.getAnnotation(TableName.class);
+                                return tableName.value();
+                            })
+                            .collect(Collectors.toSet());
+        }
+        ignoreTableName = Optional.ofNullable(ignoreTableName).orElse(new HashSet<>());
+        //兼容框架已有entity
+        ignoreTableName.addAll(classes
+                .stream()
+                .filter(aClass -> ObjectUtil.isNull(aClass.getAnnotation(TableName.class)))
+                .map(aClass -> StrUtil.toUnderlineCase(aClass.getSimpleName()))
+                .collect(Collectors.toSet()));
+    }
+
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+        // 数据权限插件
+        interceptor.addInnerInterceptor(dataPermissionInterceptor());
+        //多租户插件
+        interceptor.addInnerInterceptor(tenantLineInnerInterceptor());
+
+        // 分页插件
+        interceptor.addInnerInterceptor(paginationInnerInterceptor());
+
+        //禁止全表更新
+        interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
+
+        // 乐观锁插件
+        interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
+
+        return interceptor;
+    }
+
+    /**
+     * 数据权限插件
+     */
+    public DataPermissionInterceptor dataPermissionInterceptor() {
+        DataPermissionInterceptor dataPermissionInterceptor = new DataPermissionInterceptor();
+        CustomDataPermissionHandler customDataPermissionHandler = new CustomDataPermissionHandler();
+        dataPermissionInterceptor.setDataPermissionHandler(customDataPermissionHandler);
+        return dataPermissionInterceptor;
+    }
+
+    /**
+     * 分页插件
+     */
+    public PaginationInnerInterceptor paginationInnerInterceptor() {
+        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
+
+        // 设置数据库类型为mysql
+        paginationInnerInterceptor.setDbType(DbType.MYSQL);
+        // 设置最大单页限制数量,默认 500 条,-1 不受限制
+        paginationInnerInterceptor.setMaxLimit(-1L);
+        paginationInnerInterceptor.setDialect(new MySqlDialect());
+
+        return paginationInnerInterceptor;
+    }
+
+    /**
+     * 乐观锁插件
+     */
+    public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
+        return new OptimisticLockerInnerInterceptor();
+    }
+
+    /**
+     * 多租户插件
+     */
+    public TenantLineInnerInterceptor tenantLineInnerInterceptor() {
+        TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor();
+        tenantInterceptor.setTenantLineHandler(new TenantLineHandler() {
+            @Override
+            public Expression getTenantId() {
+                return new StringValue(tenantIdManager.getCurrentTenantId());
+            }
+
+            @Override
+            public boolean ignoreTable(String tableName) {
+                ServletRequestAttributes request = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+                //非用户请求,即来自程序自身,则忽略
+                if(request==null||request.getRequest() instanceof MockHttpServletRequest){
+                    return true;
+                }
+                StringBuffer url = request.getRequest().getRequestURL();
+                if(url.toString().endsWith("/login")||url.toString().endsWith("/getUserInfo")){
+                    return true;
+                }
+                if(CollectionUtil.isNotEmpty(ignoreTableName)&&ignoreTableName.contains(tableName)){
+                    return true;
+                }
+                // 判断当前有用户是否为系统级用户,若是,则忽略逻辑隔离
+                LoginUser loginUser = SecurityUtil.getLoginUser();
+                if(loginUser==null){
+                    //未登录
+                    return false;
+                }
+                if(Boolean.TRUE.equals(loginUser.getIsSys())){
+                    String tenantId = String.valueOf( request.getAttribute("tenantId", SCOPE_REQUEST));
+                    if(StrUtil.isNullOrUndefined(tenantId)){
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {
+                return columns.stream().anyMatch(column -> column.getColumnName().equals(tenantIdColumn));
+            }
+        });
+        return tenantInterceptor;
+    }
+
+    /**
+     * 元对象字段填充控制器
+     */
+    @Bean
+    public MetaObjectHandler metaObjectHandler() {
+        return new CreateAndUpdateMetaObjectHandler();
+    }
+
+}

+ 33 - 0
nb-framework/src/main/java/com/nb/framework/config/mybatisplus/TenantIdManager.java

@@ -0,0 +1,33 @@
+package com.nb.framework.config.mybatisplus;
+
+import cn.hutool.core.util.StrUtil;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST;
+
+/**
+ * 管理当前用户的租户ID
+ */
+@Component
+public class TenantIdManager {
+    /**
+     * 返回当前用户租户ID
+     * @return
+     */
+    public String getCurrentTenantId() {
+        //判断是否为request请求
+        ServletRequestAttributes request = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        if(request==null){
+            return "";
+        }
+        StringBuffer url = request.getRequest().getRequestURL();
+        if(url.toString().endsWith("/login")){
+            return "";
+        }
+        String tenantId = String.valueOf( request.getAttribute("tenantId", SCOPE_REQUEST));
+        return StrUtil.isNullOrUndefined(tenantId)?"":tenantId;
+    }
+
+}

+ 82 - 0
nb-framework/src/main/java/com/nb/framework/config/mybatisplus/handler/CreateAndUpdateMetaObjectHandler.java

@@ -0,0 +1,82 @@
+package com.nb.framework.config.mybatisplus.handler;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.nb.common.bo.LoginUser;
+import com.nb.common.exception.CustomException;
+import com.nb.common.result.ResultCode;
+import com.nb.common.util.SecurityUtil;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import java.util.Date;
+import java.util.Objects;
+
+import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST;
+
+/**
+ * MP注入处理器
+ *
+ * @author Kevin
+ */
+public class CreateAndUpdateMetaObjectHandler implements MetaObjectHandler {
+
+    public static final String CREATE_TIME = "createTime";
+    public static final String UPDATE_TIME = "updateTime";
+
+    public static final String CREATE_BY = "createBy";
+    public static final String UPDATE_BY = "updateBy";
+
+    public static final String IS_DELETE = "isDelete";
+
+    public static final String TENANT_ID="tenantId";
+
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        try {
+            if (metaObject.hasGetter(CREATE_TIME) && metaObject.getValue(CREATE_TIME) == null) {
+                this.strictInsertFill(metaObject, CREATE_TIME, Date.class, new Date());
+            }
+            if (metaObject.hasGetter(IS_DELETE) && metaObject.getValue(IS_DELETE) == null) {
+                this.strictInsertFill(metaObject, IS_DELETE, Integer.class,0);
+            }
+            if (metaObject.hasGetter(CREATE_BY) && metaObject.getValue(CREATE_BY) == null) {
+                LoginUser loginUser = SecurityUtil.getLoginUser();
+                this.strictInsertFill(metaObject, CREATE_BY, String.class, Objects.isNull(loginUser) ? "1" : loginUser.getSysUser().getId().toString());
+            }
+            if (metaObject.hasGetter(UPDATE_TIME) && metaObject.getValue(UPDATE_TIME) == null) {
+                this.strictUpdateFill(metaObject, UPDATE_TIME, Date.class, new Date());
+            }
+            if (metaObject.hasGetter(UPDATE_BY) && metaObject.getValue(UPDATE_BY) == null) {
+                LoginUser loginUser = SecurityUtil.getLoginUser();
+                this.strictUpdateFill(metaObject, UPDATE_BY, String.class, Objects.isNull(loginUser) ? "1" : loginUser.getSysUser().getId().toString());
+            }
+            if (metaObject.hasGetter(TENANT_ID) && metaObject.getValue(TENANT_ID) == null) {
+                ServletRequestAttributes request = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+                String tenantId ="1";
+                if(request!=null){
+                    tenantId = String.valueOf(request.getAttribute("tenantId", SCOPE_REQUEST));
+                }
+                this.strictInsertFill(metaObject, TENANT_ID, String.class, tenantId);
+            }
+        } catch (Exception e) {
+            throw new CustomException(ResultCode.INTERNAL_SERVER_ERROR.getMessage());
+        }
+    }
+
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        try {
+            if (metaObject.hasGetter(UPDATE_TIME) && metaObject.getValue(UPDATE_TIME) == null) {
+                this.strictUpdateFill(metaObject, UPDATE_TIME, Date.class, new Date());
+            }
+            if (metaObject.hasGetter(UPDATE_BY) && metaObject.getValue(UPDATE_BY) == null) {
+                LoginUser loginUser = SecurityUtil.getLoginUser();
+                this.strictUpdateFill(metaObject, UPDATE_BY, String.class, Objects.isNull(loginUser) ? null : loginUser.getSysUser().getId().toString());
+            }
+        } catch (Exception e) {
+            throw new CustomException(ResultCode.INTERNAL_SERVER_ERROR.getMessage());
+        }
+    }
+
+}

+ 135 - 0
nb-framework/src/main/java/com/nb/framework/config/mybatisplus/handler/CustomDataPermissionHandler.java

@@ -0,0 +1,135 @@
+package com.nb.framework.config.mybatisplus.handler;
+
+import cn.hutool.core.annotation.AnnotationUtil;
+import cn.hutool.core.util.ReflectUtil;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
+import com.nb.common.annotation.DataScope;
+import com.nb.common.bo.LoginUser;
+import com.nb.common.bo.SysRoleBO;
+import com.nb.common.bo.SysUserBO;
+import com.nb.common.enums.DataScopeEnum;
+import com.nb.common.util.SecurityUtil;
+import lombok.SneakyThrows;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.Function;
+import net.sf.jsqlparser.expression.LongValue;
+import net.sf.jsqlparser.expression.Parenthesis;
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
+import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
+import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
+import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
+import net.sf.jsqlparser.expression.operators.relational.InExpression;
+import net.sf.jsqlparser.schema.Column;
+import net.sf.jsqlparser.schema.Table;
+import net.sf.jsqlparser.statement.select.PlainSelect;
+import net.sf.jsqlparser.statement.select.SelectExpressionItem;
+import net.sf.jsqlparser.statement.select.SubSelect;
+
+import java.util.Collections;
+import java.util.Objects;
+
+/**
+ * 自定义数据权限
+ *
+ * @author Kevin
+ */
+public class CustomDataPermissionHandler implements DataPermissionHandler {
+
+    /**
+     * 构建过滤条件
+     *
+     * @param user  当前登录用户
+     * @param where 当前查询条件
+     * @return 构建后查询条件
+     */
+    public static Expression dataScopeFilter(SysUserBO user, DataScope data, Expression where) {
+        String tableAlias = data.tableAlias();
+        String deptColumnName = data.deptColumnName();
+        String userColumnName = data.userColumnName();
+        Expression expression = null;
+        for (SysRoleBO role : user.getRoles()) {
+            String dataScope = role.getDataScope();
+            if (DataScopeEnum.ALL.getCode().equals(dataScope)) {
+                return where;
+            }
+            if (DataScopeEnum.CUSTOM.getCode().equals(dataScope)) {
+                InExpression inExpression = new InExpression();
+                inExpression.setLeftExpression(buildColumn(tableAlias, deptColumnName));
+                SubSelect subSelect = new SubSelect();
+                PlainSelect select = new PlainSelect();
+                select.setSelectItems(Collections.singletonList(new SelectExpressionItem(new Column("dept_id"))));
+                select.setFromItem(new Table("sys_role_dept"));
+                EqualsTo equalsTo = new EqualsTo();
+                equalsTo.setLeftExpression(new Column("role_id"));
+                equalsTo.setRightExpression(new LongValue(role.getId()));
+                select.setWhere(equalsTo);
+                subSelect.setSelectBody(select);
+                inExpression.setRightExpression(subSelect);
+                expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, inExpression) : inExpression;
+            }
+            if (DataScopeEnum.DEPT.getCode().equals(dataScope)) {
+                EqualsTo equalsTo = new EqualsTo();
+                equalsTo.setLeftExpression(buildColumn(tableAlias, deptColumnName));
+                equalsTo.setRightExpression(new LongValue(user.getDeptId()));
+                expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, equalsTo) : equalsTo;
+            }
+            if (DataScopeEnum.DEPT_AND_CHILD.getCode().equals(dataScope)) {
+                InExpression inExpression = new InExpression();
+                inExpression.setLeftExpression(buildColumn(tableAlias, deptColumnName));
+                SubSelect subSelect = new SubSelect();
+                PlainSelect select = new PlainSelect();
+                select.setSelectItems(Collections.singletonList(new SelectExpressionItem(new Column("dept_id"))));
+                select.setFromItem(new Table("sys_dept"));
+                EqualsTo equalsTo = new EqualsTo();
+                equalsTo.setLeftExpression(new Column("dept_id"));
+                equalsTo.setRightExpression(new LongValue(user.getDeptId()));
+                Function function = new Function();
+                function.setName("find_in_set");
+                function.setParameters(new ExpressionList(new LongValue(user.getDeptId()), new Column("ancestors")));
+                select.setWhere(new OrExpression(equalsTo, function));
+                subSelect.setSelectBody(select);
+                inExpression.setRightExpression(subSelect);
+                expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, inExpression) : inExpression;
+            }
+            if (DataScopeEnum.SELF.getCode().equals(dataScope)) {
+                EqualsTo equalsTo = new EqualsTo();
+                equalsTo.setLeftExpression(buildColumn(tableAlias, userColumnName));
+                equalsTo.setRightExpression(new LongValue(user.getId()));
+                expression = ObjectUtils.isNotEmpty(expression) ? new OrExpression(expression, equalsTo) : equalsTo;
+            }
+        }
+        return ObjectUtils.isNotEmpty(where) ? new AndExpression(where, new Parenthesis(expression)) : expression;
+    }
+
+    /**
+     * 构建Column
+     *
+     * @param tableAlias 表别名
+     * @param columnName 字段名称
+     * @return 带表别名字段
+     */
+    public static Column buildColumn(String tableAlias, String columnName) {
+        if (StringUtils.isNotEmpty(tableAlias)) {
+            columnName = tableAlias + "." + columnName;
+        }
+        return new Column(columnName);
+    }
+
+    @Override
+    @SneakyThrows
+    public Expression getSqlSegment(Expression where, String mappedStatementId) {
+        Class<?> clazz = Class.forName(mappedStatementId.substring(0, mappedStatementId.lastIndexOf(".")));
+        String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(".") + 1);
+        DataScope dataScope = AnnotationUtil.getAnnotation(ReflectUtil.getMethodByName(clazz, methodName), DataScope.class);
+        if (Objects.isNull(dataScope)) {
+            return where;
+        }
+        LoginUser loginUser = SecurityUtil.getLoginUser();
+        if (Objects.isNull(loginUser) || SecurityUtil.isSuperAdmin()) {
+            return where;
+        }
+        return dataScopeFilter(loginUser.getSysUser(), dataScope, where);
+    }
+}

+ 77 - 0
nb-framework/src/main/java/com/nb/framework/config/properties/DruidProperties.java

@@ -0,0 +1,77 @@
+package com.nb.framework.config.properties;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * druid 配置属性
+ *
+ * @author Kevin
+ */
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "spring.datasource.druid")
+public class DruidProperties {
+
+    /**
+     * 初始连接数
+     */
+    private int initialSize;
+    /**
+     * 最小连接池数量
+     */
+    private int minIdle;
+    /**
+     * 最大连接池数量
+     */
+    private int maxActive;
+    /**
+     * 配置获取连接等待超时的时间
+     */
+    private int maxWait;
+    /**
+     * 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+     */
+    private int timeBetweenEvictionRunsMillis;
+    /**
+     * 配置一个连接在池中最小生存的时间,单位是毫秒
+     */
+    private int minEvictableIdleTimeMillis;
+    /**
+     * 配置一个连接在池中最大生存的时间,单位是毫秒
+     */
+    private int maxEvictableIdleTimeMillis;
+    /**
+     * 配置检测连接是否有效
+     */
+    private String validationQuery;
+    /**
+     * 初始连接数
+     */
+    private boolean testWhileIdle;
+    /**
+     * 初始连接数
+     */
+    private boolean testOnBorrow;
+    /**
+     * 初始连接数
+     */
+    private boolean testOnReturn;
+
+    public DruidDataSource dataSource(DruidDataSource datasource) {
+        datasource.setInitialSize(initialSize);
+        datasource.setMaxActive(maxActive);
+        datasource.setMinIdle(minIdle);
+        datasource.setMaxWait(maxWait);
+        datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
+        datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
+        datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
+        datasource.setValidationQuery(validationQuery);
+        datasource.setTestWhileIdle(testWhileIdle);
+        datasource.setTestOnBorrow(testOnBorrow);
+        datasource.setTestOnReturn(testOnReturn);
+        return datasource;
+    }
+}

+ 25 - 0
nb-framework/src/main/java/com/nb/framework/datasource/DynamicDataSource.java

@@ -0,0 +1,25 @@
+package com.nb.framework.datasource;
+
+import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
+
+import javax.sql.DataSource;
+import java.util.Map;
+
+/**
+ * 动态数据源
+ *
+ * @author Kevin
+ */
+public class DynamicDataSource extends AbstractRoutingDataSource {
+
+    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
+        super.setDefaultTargetDataSource(defaultTargetDataSource);
+        super.setTargetDataSources(targetDataSources);
+        super.afterPropertiesSet();
+    }
+
+    @Override
+    protected Object determineCurrentLookupKey() {
+        return DynamicDataSourceContextHolder.getDataSourceType();
+    }
+}

+ 40 - 0
nb-framework/src/main/java/com/nb/framework/datasource/DynamicDataSourceContextHolder.java

@@ -0,0 +1,40 @@
+package com.nb.framework.datasource;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * 数据源切换处理
+ *
+ * @author Kevin
+ */
+@Slf4j
+public class DynamicDataSourceContextHolder {
+
+    /**
+     * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
+     * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
+     */
+    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
+
+    /**
+     * 获得数据源的变量
+     */
+    public static String getDataSourceType() {
+        return CONTEXT_HOLDER.get();
+    }
+
+    /**
+     * 设置数据源的变量
+     */
+    public static void setDataSourceType(String dsType) {
+        log.info("切换到{}数据源", dsType);
+        CONTEXT_HOLDER.set(dsType);
+    }
+
+    /**
+     * 清空数据源变量
+     */
+    public static void clearDataSourceType() {
+        CONTEXT_HOLDER.remove();
+    }
+}

+ 29 - 0
nb-framework/src/main/java/com/nb/framework/satoken/service/CustomStpInterface.java

@@ -0,0 +1,29 @@
+package com.nb.framework.satoken.service;
+
+import cn.dev33.satoken.stp.StpInterface;
+import com.nb.common.bo.SysRoleBO;
+import com.nb.common.util.SecurityUtil;
+import com.google.common.collect.Lists;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+/**
+ * 自定义SaToken权限验证
+ *
+ * @author Kevin
+ */
+@Component
+public class CustomStpInterface implements StpInterface {
+    @Override
+    public List<String> getPermissionList(Object loginId, String loginType) {
+        return Lists.newArrayList(SecurityUtil.getLoginUser().getPermissions());
+    }
+
+    @Override
+    public List<String> getRoleList(Object loginId, String loginType) {
+        return SecurityUtil.getLoginUser().getSysUser().getRoles().stream().map(SysRoleBO::getRoleCode).collect(Collectors.toList());
+    }
+}

+ 163 - 0
nb-framework/src/main/java/com/nb/framework/web/exception/GlobalExceptionHandler.java

@@ -0,0 +1,163 @@
+package com.nb.framework.web.exception;
+
+import cn.dev33.satoken.exception.NotLoginException;
+import cn.dev33.satoken.exception.NotPermissionException;
+import cn.dev33.satoken.exception.NotRoleException;
+import com.nb.common.exception.*;
+import com.nb.common.result.R;
+import com.nb.common.result.ResultCode;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.BindException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import javax.validation.ConstraintViolationException;
+import java.util.Objects;
+
+/**
+ * 全局异常处理器
+ *
+ * @author Kevin
+ */
+@RestControllerAdvice
+@Slf4j
+public class GlobalExceptionHandler {
+
+    /**
+     * 业务异常
+     */
+    @ExceptionHandler(CustomException.class)
+    public R businessException(CustomException e) {
+        if (Objects.isNull(e.getCode())) {
+            return R.fail(e.getMessage());
+        }
+        return R.result(e.getCode(), e.getMessage());
+    }
+
+    /**
+     * 业务异常
+     */
+    @ExceptionHandler(ScriptException.class)
+    public R businessException(ScriptException e) {
+        return R.success(e.getExecuteResult());
+    }
+
+    /**
+     * 业务异常
+     */
+    @ExceptionHandler(TenantException.class)
+    public R tenantException(TenantException e) {
+        return R.success(ResultCode.TENANT_NONE);
+    }
+
+    @ExceptionHandler(NotLoginException.class)
+    public R handleNotLoginException(NotLoginException e) {
+        log.error(e.getMessage(), e);
+        return R.result(ResultCode.TOKEN_ERROR);
+    }
+
+    @ExceptionHandler(NotRoleException.class)
+    public R handleNotRoleException(NotRoleException e) {
+        log.error(e.getMessage(), e);
+        return R.result(ResultCode.UN_AUTHORIZED);
+    }
+
+    @ExceptionHandler(NotPermissionException.class)
+    public R handleNotPermissionException(NotPermissionException e) {
+        log.error(e.getMessage(), e);
+        return R.result(ResultCode.UN_AUTHORIZED);
+    }
+
+    @ExceptionHandler(RuntimeException.class)
+    public R handleRuntimeException(RuntimeException e) {
+        log.error(e.getMessage(), e);
+//        return R.result(ResultCode.INTERNAL_SERVER_ERROR);
+        return R.fail(e.getLocalizedMessage());
+    }
+
+    /**
+     * 在短时间内重复发送同一请求
+     */
+    @ExceptionHandler(RequestRepeatException.class)
+    public R businessException(RequestRepeatException e) {
+        return R.result(ResultCode.REQUEST_REPEAT, ResultCode.REQUEST_REPEAT.getMessage());
+    }
+
+    /**
+     * 请求由于时区问题失效
+     */
+    @ExceptionHandler(RequestTimeOutException.class)
+    public R businessException(RequestTimeOutException e) {
+        return R.result(ResultCode.TIME_ERROR, "时区错误");
+    }
+
+    /**
+     * 签名失败
+     */
+    @ExceptionHandler(RequestSignErrorException.class)
+    public R businessException(RequestSignErrorException e) {
+        return R.result(ResultCode.PARAM_VALID_ERROR,"签名错误");
+    }
+
+    /**
+     * 请求头参数错误
+     */
+    @ExceptionHandler(RequestParmErrorException.class)
+    public R businessException(RequestParmErrorException e) {
+        return R.result(ResultCode.PARAM_MISS, ResultCode.PARAM_MISS.getMessage());
+    }
+
+    @ExceptionHandler(Exception.class)
+    public R handleException(Exception e) {
+        log.error(e.getMessage(), e);
+//        return R.result(ResultCode.INTERNAL_SERVER_ERROR);
+        return R.fail(e.getLocalizedMessage());
+    }
+
+    /**
+     * 自定义验证异常
+     */
+    @ExceptionHandler(BindException.class)
+    public R validatedBindException(BindException e) {
+        log.error(e.getMessage(), e);
+        String message = e.getAllErrors().get(0).getDefaultMessage();
+        return R.fail(message);
+    }
+
+    /**
+     * 自定义验证异常
+     */
+    @ExceptionHandler(ConstraintViolationException.class)
+    public R constraintViolationException(ConstraintViolationException e) {
+        log.error(e.getMessage(), e);
+        String message = e.getConstraintViolations().iterator().next().getMessage();
+        return R.fail(message);
+    }
+
+    /**
+     * 自定义验证异常
+     */
+    @ExceptionHandler(MethodArgumentNotValidException.class)
+    public Object validExceptionHandler(MethodArgumentNotValidException e) {
+        log.error(e.getMessage(), e);
+        String message = e.getBindingResult().getFieldError().getDefaultMessage();
+        return R.fail(message);
+    }
+
+    /**
+     * 在短时间内重复发送同一请求
+     */
+    @ExceptionHandler(CaptchaException.class)
+    public R captchaHandler(CaptchaException e) {
+        return R.result(ResultCode.CAPTCHA, e.getMessage());
+    }
+
+    /**
+     * 演示模式异常
+     */
+    @ExceptionHandler(DemoModeException.class)
+    public R demoModeException(DemoModeException e) {
+        return R.fail("演示模式,不允许操作");
+    }
+}

+ 61 - 0
nb-framework/src/main/java/com/nb/framework/web/service/IUserService.java

@@ -0,0 +1,61 @@
+package com.nb.framework.web.service;
+
+import com.nb.common.dto.LoginDTO;
+import com.nb.system.common.vo.AccountInfoVO;
+import com.nb.system.common.vo.RouteItemVO;
+import com.nb.system.common.vo.UserInfoVO;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 用户接口
+ *
+ * @author Kevin
+ */
+public interface IUserService {
+
+    /**
+     * 登录接口
+     *
+     * @param req
+     * @return
+     */
+    String login(LoginDTO req);
+
+    /**
+     * 获取用户信息
+     *
+     * @return
+     */
+    UserInfoVO getUserInfo();
+
+    /**
+     * 获取权限标识
+     *
+     * @return
+     */
+    Set<String> getPermCode();
+
+    /**
+     * 获取菜单列表
+     *
+     * @return
+     */
+    List<RouteItemVO> getMenuList();
+
+    /**
+     * 获取账户信息
+     *
+     * @return
+     */
+    AccountInfoVO getAccountInfo();
+
+    /**
+     * 更新账户信息
+     *
+     * @param req
+     */
+    void saveAccountInfo(AccountInfoVO req);
+
+}

+ 294 - 0
nb-framework/src/main/java/com/nb/framework/web/service/impl/UserServiceImpl.java

@@ -0,0 +1,294 @@
+package com.nb.framework.web.service.impl;
+
+import cn.dev33.satoken.spring.SpringMVCUtil;
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.useragent.UserAgent;
+import cn.hutool.http.useragent.UserAgentUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.nb.common.Constants;
+import com.nb.common.MenuConstants;
+import com.nb.common.bo.LoginUser;
+import com.nb.common.bo.SysRoleBO;
+import com.nb.common.bo.SysUserBO;
+import com.nb.common.dto.LoginDTO;
+import com.nb.common.enums.*;
+import com.nb.common.exception.CustomException;
+import com.nb.common.util.AddressUtil;
+import com.nb.common.util.IpUtil;
+import com.nb.common.util.SecurityUtil;
+import com.nb.framework.web.service.IUserService;
+import com.nb.system.common.vo.*;
+import com.nb.system.entity.SysMenu;
+import com.nb.system.entity.SysRole;
+import com.nb.system.entity.SysUser;
+import com.nb.system.properties.CaptchaProperties;
+import com.nb.system.service.ISysMenuService;
+import com.nb.system.service.ISysRoleService;
+import com.nb.system.service.ISysUserService;
+import com.nb.system.utils.CaptchaTool;
+import com.google.common.collect.Sets;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 用户实现类
+ *
+ * @author Kevin
+ */
+@Slf4j
+@Service
+@EnableConfigurationProperties(CaptchaProperties.class)
+public class UserServiceImpl implements IUserService {
+
+    @Resource
+    private ISysMenuService sysMenuService;
+
+    @Resource
+    private CaptchaTool captchaTool;
+
+    @Resource
+    private CaptchaProperties captchaProperties;
+
+    @Resource
+    private ISysUserService sysUserService;
+
+    @Resource
+    private ISysRoleService sysRoleService;
+
+    @Override
+    public String login(LoginDTO req) {
+        if (StrUtil.isBlank(req.getGrantType())) {
+            throw new CustomException("授权类型不能为空");
+        }
+        if (!GrantTypeEnum.contains(req.getGrantType())) {
+            throw new CustomException("授权类型暂不支持");
+        }
+        SysUser sysUser = null;
+        if (req.getGrantType().equals(GrantTypeEnum.USERNAME_PASSWORD.getCode())) {
+            if (StrUtil.isBlank(req.getUsername())) {
+                throw new CustomException("用户名不能为空");
+            }
+            if (StrUtil.isBlank(req.getPassword())) {
+                throw new CustomException("密码不能为空");
+            }
+            if(captchaProperties.isEnable()){
+                HttpServletRequest request = SpringMVCUtil.getRequest();
+                String requestFrom = request.getHeader("RequestFrom");
+                //来自app的请求不需要验证码
+                if(!"TuoRenApp".equals(requestFrom)){
+                    captchaTool.ver(req.getCodeKey(),req.getCode());
+                }
+            }
+            sysUser = sysUserService.getOne(Wrappers.lambdaQuery(SysUser.class).eq(SysUser::getAccount, req.getUsername()));
+            if (Objects.isNull(sysUser)) {
+                log.info("登录用户:{}不存在", req.getUsername());
+                throw new CustomException("登录用户不存在");
+            }
+            if (!SecurityUtil.matchesPassword(req.getPassword(), sysUser.getPassword())) {
+                throw new CustomException("账号或密码不正确");
+            }
+            if (sysUser.getDelFlag().equals(DelFlagEnum.YES.getCode())) {
+                log.info("登录用户:{}已被删除", req.getUsername());
+                throw new CustomException("对不起,您的账号已被删除");
+            }
+            if (sysUser.getStatus().equals(StatusEnum.NO.getCode())) {
+                log.info("登录用户:{}已被停用", req.getUsername());
+                throw new CustomException("对不起,您的账号已被停用");
+            }
+        }
+        if (req.getGrantType().equals(GrantTypeEnum.MOBILE_CODE.getCode())) {
+            if (StrUtil.isBlank(req.getMobile())) {
+                throw new CustomException("手机号不能为空");
+            }
+            if (StrUtil.isBlank(req.getCode())) {
+                throw new CustomException("验证码不能为空");
+            }
+            // TODO,短信验证码校验,自行实现
+            if (!Objects.equals(req.getCode(), Constants.DEFAULT_SMS_CODE)) {
+                throw new CustomException("账号或密码不正确");
+            }
+            sysUser = sysUserService.getOne(Wrappers.lambdaQuery(SysUser.class).eq(SysUser::getPhone, req.getMobile()));
+            if (Objects.isNull(sysUser)) {
+                log.info("登录用户:{}不存在", req.getUsername());
+                throw new CustomException("登录用户不存在");
+            }
+            if (sysUser.getDelFlag().equals(DelFlagEnum.YES.getCode())) {
+                log.info("登录用户:{}已被删除", req.getUsername());
+                throw new CustomException("对不起,您的账号已被删除");
+            }
+            if (sysUser.getStatus().equals(StatusEnum.NO.getCode())) {
+                log.info("登录用户:{}已被停用", req.getUsername());
+                throw new CustomException("对不起,您的账号已被停用");
+            }
+        }
+        log.info("登录用户:{}", req.getUsername());
+        SysUserBO sysUserBO = BeanUtil.copyProperties(sysUser, SysUserBO.class);
+        // 查询角色列表
+        List<SysRole> sysRoleList = sysRoleService.listSysRoleByUserId(sysUser.getId());
+        // 设置角色列表
+        sysUserBO.setRoles(sysRoleList.stream().map(item -> BeanUtil.copyProperties(item, SysRoleBO.class)).collect(Collectors.toList()));
+        // 查询权限标识
+        Set<String> permissions = Sets.newHashSet();
+        if (CollUtil.isNotEmpty(sysRoleList)&&
+                sysRoleList.stream().anyMatch(sysRole -> "admin".equalsIgnoreCase(sysRole.getRoleCode()))) {
+            permissions.add(Constants.ALL_PERMISSION);
+        } else {
+            permissions = sysMenuService.getPermissionsByUserId(sysUser.getId());
+        }
+        // 登录
+        StpUtil.login(sysUser.getId());
+        LoginUser loginUser = new LoginUser();
+        loginUser.setToken(StpUtil.getTokenValue());
+        loginUser.setUserPlatform(UserPlatformEnum.WEB.getCode());
+        loginUser.setGrantType(req.getGrantType());
+        loginUser.setSysUser(sysUserBO);
+        loginUser.setIsSys(sysUser.getIsSys());
+        if (req.getGrantType().equals(GrantTypeEnum.USERNAME_PASSWORD.getCode())) {
+            loginUser.setUsername(req.getUsername());
+        }
+        if (req.getGrantType().equals(GrantTypeEnum.MOBILE_CODE.getCode())) {
+            loginUser.setUsername(req.getMobile());
+        }
+        loginUser.setLoginTime(new Date());
+
+        HttpServletRequest request = SpringMVCUtil.getRequest();
+        UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
+        String ipAddress = IpUtil.getClientIp(request);
+        loginUser.setIpAddress(ipAddress);
+        loginUser.setLoginLocation(AddressUtil.getRealAddressByIp(ipAddress));
+        loginUser.setBrowser(userAgent.getBrowser().getName());
+        loginUser.setOs(userAgent.getOs().getName());
+        loginUser.setPermissions(permissions);
+        // 设置用户信息
+        StpUtil.getTokenSession().set(Constants.LOGIN_USER_KEY, loginUser);
+        return StpUtil.getTokenValue();
+    }
+
+    @Override
+    public UserInfoVO getUserInfo() {
+        UserInfoVO userInfoVO = new UserInfoVO();
+        SysUser sysUser = sysUserService.getById(StpUtil.getLoginIdAsLong());
+        // 查询角色列表
+        List<SysRole> sysRoleList = sysRoleService.listSysRoleByUserId(sysUser.getId());
+        List<RoleInfoVO> roleInfoVOList = sysRoleList.stream().map(item -> {
+            RoleInfoVO roleInfoVO = new RoleInfoVO();
+            roleInfoVO.setRoleName(item.getRoleName());
+            roleInfoVO.setValue(item.getRoleCode());
+            return roleInfoVO;
+        }).collect(Collectors.toList());
+        userInfoVO.setUserId(sysUser.getId().toString());
+        userInfoVO.setUsername(sysUser.getAccount());
+        userInfoVO.setRealName(sysUser.getNickname());
+        userInfoVO.setAvatar(sysUser.getAvatar());
+        userInfoVO.setDesc(sysUser.getRemarks());
+        userInfoVO.setRoles(roleInfoVOList);
+        userInfoVO.setIsSys(sysUser.getIsSys());
+        userInfoVO.setTenantId(String.valueOf(sysUser.getTenantId()));
+        userInfoVO.setTenantName(sysUser.getTenantName());
+        return userInfoVO;
+    }
+
+    @Override
+    public Set<String> getPermCode() {
+        return SecurityUtil.getLoginUser().getPermissions();
+    }
+
+    @Override
+    public List<RouteItemVO> getMenuList() {
+        List<SysMenu> sysMenuList;
+        SysUserBO sysUserBO = SecurityUtil.getSysUser();
+        if (SecurityUtil.isSuperAdmin()) {
+            LambdaQueryWrapper<SysMenu> queryWrapper = Wrappers.lambdaQuery();
+            queryWrapper.eq(SysMenu::getStatus, StatusEnum.YES.getCode());
+            queryWrapper.in(SysMenu::getMenuType, MenuTypeEnum.DIR.getCode(), MenuTypeEnum.MENU.getCode());
+            queryWrapper.eq(!SecurityUtil.isSys(),SysMenu::getTenantMenu,true);
+            sysMenuList = sysMenuService.list(queryWrapper);
+        } else {
+            sysMenuList = sysMenuService.listGrantMenuByUserId(sysUserBO.getId());
+        }
+        List<RouteItemVO> routeItemVOList = sysMenuList.stream()
+                .filter(item -> item.getParentId().intValue() == 0)
+                .sorted(Comparator.comparing(SysMenu::getSort))
+                .map(item -> {
+                    RouteItemVO node = convertToRoute(item);
+                    node.setChildren(getChildrenList(item, sysMenuList));
+                    return node;
+                }).collect(Collectors.toList());
+        return routeItemVOList;
+    }
+
+    @Override
+    public AccountInfoVO getAccountInfo() {
+        LoginUser loginUser = SecurityUtil.getLoginUser();
+        SysUser sysUser = sysUserService.getById(loginUser.getSysUser().getId());
+        return BeanUtil.copyProperties(sysUser, AccountInfoVO.class);
+    }
+
+    @Override
+    public void saveAccountInfo(AccountInfoVO req) {
+        LoginUser loginUser = SecurityUtil.getLoginUser();
+        SysUser entity = BeanUtil.copyProperties(req, SysUser.class);
+        entity.setId(loginUser.getSysUser().getId());
+        sysUserService.updateById(entity);
+    }
+
+    private List<RouteItemVO> getChildrenList(SysMenu root, List<SysMenu> list) {
+        List<RouteItemVO> childrenList = list.stream()
+                .filter(item -> item.getParentId().equals(root.getId()))
+                .sorted(Comparator.comparing(SysMenu::getSort))
+                .map(item -> {
+                    RouteItemVO node = convertToRoute(item);
+                    node.setChildren(getChildrenList(item, list));
+                    return node;
+                }).collect(Collectors.toList());
+        return childrenList;
+    }
+
+    private RouteItemVO convertToRoute(SysMenu item) {
+        RouteItemVO node = new RouteItemVO();
+        node.setName(StrUtil.upperFirst(item.getRoutePath()));
+        node.setPath(item.getRoutePath());
+        node.setComponent(item.getComponent());
+        // 一级目录
+        if (Objects.equals(item.getMenuType(), MenuTypeEnum.DIR.getCode()) && item.getParentId().intValue() == 0) {
+            node.setPath("/" + item.getRoutePath());
+            node.setComponent(MenuConstants.LAYOUT);
+        }
+        // 外部链接
+        if (Objects.equals(item.getMenuType(), MenuTypeEnum.MENU.getCode()) && Objects.equals(item.getLinkExternal(), LinkExternalEnum.YES.getCode())) {
+            node.setComponent(MenuConstants.IFRAME);
+        }
+        RouteMetoVO routeMetoVO = new RouteMetoVO();
+        routeMetoVO.setTitle(item.getMenuName());
+        routeMetoVO.setIcon(item.getIcon());
+        routeMetoVO.setHideMenu(StrUtil.isNotBlank(item.getVisible()) && item.getVisible().equals(VisibleEnum.HIDE.getCode()));
+        // 菜单
+        if (Objects.equals(item.getMenuType(), MenuTypeEnum.MENU.getCode())) {
+            routeMetoVO.setIgnoreKeepAlive(item.getKeepalive().equals(KeepaliveEnum.YES.getCode()));
+        }
+        // 外部链接
+        if (Objects.equals(item.getMenuType(), MenuTypeEnum.MENU.getCode()) && Objects.equals(item.getLinkExternal(), LinkExternalEnum.YES.getCode())) {
+            // 内嵌
+            if (Objects.equals(item.getFrame(), FrameEnum.YES.getCode())) {
+                routeMetoVO.setFrameSrc(item.getLinkUrl());
+            }
+            // 外嵌
+            if (item.getFrame().equals(FrameEnum.NO.getCode())) {
+                node.setPath(item.getLinkUrl());
+            }
+        }
+        node.setMeta(routeMetoVO);
+        return node;
+    }
+
+}

+ 228 - 0
nb-system/src/main/java/com/nb/aliyun/AliyunConsumerGroupService.java

@@ -0,0 +1,228 @@
+package com.nb.aliyun;
+
+import cn.hutool.core.text.CharSequenceUtil;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.nb.aliyun.utils.Constants;
+import com.nb.aliyun.utils.EnumUtils;
+import com.nb.aliyun.utils.Items;
+import com.nb.bus.bean.AliIotConfig;
+import com.nb.bus.entity.BusDeviceEntity;
+import com.nb.bus.entity.BusDeviceRunningEntity;
+import com.nb.bus.entity.BusHospitalLogEntity;
+import com.nb.bus.enums.HospitalLogEnum;
+import com.nb.bus.listener.event.bean.DeviceInfoEvent;
+import com.nb.bus.service.LocalBusDeviceService;
+import com.nb.bus.service.LocalBusHospitalLogService;
+import com.nb.bus.websocket.listener.DeviceInfoListener;
+import com.nb.common.util.ExceptionUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.PostConstruct;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import java.util.Date;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @Author 龙三郎
+ * @Date 2022-4-06 16:22:13
+ * @Version 1.0
+ * @Description 阿里云物联网平台服务端订阅
+ */
+@Service
+@Slf4j
+public class AliyunConsumerGroupService {
+    private static final String IOTID = "iotId";
+    private static final String PRODUCTKEY = "productKey";
+    private static final String DEVICENAME = "deviceName";
+    private static final String TOPIC = "topic";
+    private static final String MESSAGEID = "messageId";
+    private static final String ITEMS = "items";
+    private static final String VALUE = "value";
+    private static final String STATUS = "status";
+    private static final String CONTENT = "content";
+
+    @Autowired
+    @Lazy
+    private AliyunIotSubscribeClient client;
+    @Autowired
+    @Lazy
+    private LocalBusDeviceService deviceService;
+
+    @Autowired
+    private DeviceInfoListener deviceInfoListener;
+    @Autowired
+    @Lazy
+    private LocalBusHospitalLogService hospitalLogService;
+    @Value("${aliyun.server-subscription.enable:false}")
+    private boolean isEnable;
+
+    // 开启服务端订阅
+    @PostConstruct
+    public void subscribe(){
+        if (!isEnable){
+            log.info("订阅禁止");
+            return;
+        }
+        log.info("允许开启订阅");
+        try {
+            // 开启订阅
+            client.start(messageListener);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        log.info("阿里云物联网订阅成功。。。。。。。。。。。");
+    }
+
+
+
+    /**
+     * 描述:业务处理异步线程池,线程池参数可以根据您的业务特点调整,或者您也可以用其他异步方式处理接收到的消息。
+     */
+    private final static ExecutorService executorService = new ThreadPoolExecutor(
+            Runtime.getRuntime().availableProcessors(),
+            Runtime.getRuntime().availableProcessors() * 2, 60, TimeUnit.SECONDS,
+            new LinkedBlockingQueue(50000),
+            new AliYunThreadFactory());
+
+    static class AliYunThreadFactory implements ThreadFactory {
+        static final AtomicInteger poolNumber = new AtomicInteger(1);
+        final ThreadGroup group;
+        final AtomicInteger threadNumber = new AtomicInteger(1);
+        final String namePrefix;
+
+        AliYunThreadFactory() {
+            SecurityManager s = System.getSecurityManager();
+            group = (s != null)? s.getThreadGroup() :
+                    Thread.currentThread().getThreadGroup();
+            namePrefix = "ali-iot-" +
+                    poolNumber.getAndIncrement();
+        }
+        @Override
+        public Thread newThread(Runnable r) {
+            Thread t = new Thread(group, r,
+                    namePrefix + threadNumber.getAndIncrement(),
+                    0);
+            if (t.isDaemon()){
+                t.setDaemon(false);
+            }
+            if (t.getPriority() != Thread.NORM_PRIORITY){
+                t.setPriority(Thread.NORM_PRIORITY);
+            }
+            return t;
+        }
+    }
+
+    private MessageListener messageListener = (message) -> {
+        try {
+            //1.收到消息之后一定要ACK。
+            // 推荐做法:创建Session选择Session.AUTO_ACKNOWLEDGE,这里会自动ACK。
+            // 其他做法:创建Session选择Session.CLIENT_ACKNOWLEDGE,这里一定要调message.acknowledge()来ACK。
+            // message.acknowledge();
+            //2.建议异步处理收到的消息,确保onMessage函数里没有耗时逻辑。
+            // 如果业务处理耗时过程过长阻塞住线程,可能会影响SDK收到消息后的正常回调。
+            executorService.submit(()-> processMessage(message));
+        } catch (Exception e) {
+            log.error("submit task occurs exception ", e);
+        }
+    };
+
+    private void processMessage(Message message) {
+        BusHospitalLogEntity hospitalLog = new BusHospitalLogEntity();
+        hospitalLog.setType(HospitalLogEnum.ALI);
+        long startTime = System.currentTimeMillis();
+        String deviceName=null;
+        try {
+            // 获取主题,消息id和内容
+            String topic = message.getStringProperty(TOPIC);
+            String messageId = message.getStringProperty(MESSAGEID);
+            hospitalLog.setMsgId(messageId);
+            JSONObject content = JSON.parseObject(new String(message.getBody(byte[].class)));
+            log.info("阿里云物联网发送的数据:"+JSON.toJSONString(content));
+            // 设备名称
+            deviceName = content.getString(DEVICENAME);
+            hospitalLog.setIdentityCode(deviceName);
+            hospitalLog.setInput(content.toJSONString());
+            // 创建设备对象
+            Date now = new Date();
+            // 根据topic判断数据类型,设备上下线状态
+            if (topic.matches("^/as/mqtt/status/[\\w\\/]*")){
+                BusDeviceEntity device = new BusDeviceEntity();
+                device.setDeviceId(deviceName);
+                String status = content.getString(STATUS);
+                device.setStatus(EnumUtils.getDeviceStatusEnum2(status));
+                if ("online".equalsIgnoreCase(status)){
+                    log.info(deviceName+"设备【{}】上线",deviceName);
+                }else if ("offline".equalsIgnoreCase(status)){
+                    log.info(deviceName+"设备【{}】下线",deviceName);
+                }else {
+                    log.info(deviceName+"设备【{}】未激活",deviceName);
+                }
+                // 更新设备状态
+                deviceService.updateDevice(device);
+            }else if (topic.matches("[\\w\\/]*event/property/post$")){//设备属性上报
+                // 设备属性集合
+                Items items = new Items(content.getJSONObject("items"));
+                log.info("上传设备属性:【{}】",items);
+                BusDeviceRunningEntity deviceRunning = new BusDeviceRunningEntity();
+                deviceRunning.updateFieldsByItems(deviceName,items);
+                hospitalLog.setTenantId(deviceInfoListener.deviceInfoDetail(new DeviceInfoEvent(this,deviceRunning,deviceName)));
+            }else if(topic.matches("[\\w\\/]+thing/lifecycle$")){// 设备生命周期
+                // 获取生命周期类型
+                String action = content.getString("action");
+                if ("create".equals(action)){
+                    // 创建设备
+                    BusDeviceEntity device = new BusDeviceEntity();
+                    device.setDeviceId(deviceName);
+                    device.setCreateTime(now);
+                    device.setCreateBy(Constants.DefaultCreateBy);
+                    device.setUpdateTime(now);
+                    device.setUpdateBy(Constants.DefaultUpdateBy);
+                    device.setTenantId(Constants.DefaultHospital);
+
+                    // 配置信息
+                    AliIotConfig config = new AliIotConfig();
+                    config.setDeviceName(deviceName);
+                    config.setDeviceSecret(content.getString("deviceSecret"));
+                    config.setIotId(content.getString("iotId"));
+                    config.setProductKey(content.getString("productKey"));
+
+                    // 设置配置信息
+                    device.setConfig(config);
+                    deviceService.saveDevice(device);
+                }else if ("delete".equals(action)){
+                    // 删除设备
+                    deviceService.removeByDeviceId(deviceName);
+                }
+            }else {
+                log.warn("阿里云数据【{}】,未知的topic:【{}】",content.toJSONString(),topic);
+            }
+            hospitalLog.setSuccess(true);
+        } catch (Exception e) {
+            hospitalLog.setSuccess(false);
+            hospitalLog.setMessage(ExceptionUtil.getExceptionMsg(e));
+            log.error("阿里云数据【{}】数据处理失败 ", JSONUtil.toJsonStr(message), e);
+        }finally {
+            if(CharSequenceUtil.isNotBlank(deviceName)&&CharSequenceUtil.isEmpty(hospitalLog.getTenantId())){
+                BusDeviceEntity device = deviceService.getByDeviceId(deviceName);
+                hospitalLog.setTenantId(device.getTenantId());
+            }
+            long entTime = System.currentTimeMillis();
+            hospitalLog.setUseTime(entTime-startTime);
+            hospitalLogService.save(hospitalLog);
+        }
+
+    }
+
+
+
+
+
+}

+ 204 - 0
nb-system/src/main/java/com/nb/aliyun/AliyunIotSubscribeClient.java

@@ -0,0 +1,204 @@
+package com.nb.aliyun;
+
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.qpid.jms.JmsConnection;
+import org.apache.qpid.jms.JmsConnectionListener;
+import org.apache.qpid.jms.message.JmsInboundMessageDispatch;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.Async;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import javax.jms.*;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+
+/**
+ * @Author 龙三郎
+ * @Date 2021-06-17 10:53:30
+ * @Version 1.0
+ * @Description XXX
+ */
+@Configuration
+@EnableConfigurationProperties(PlatformAccount.class)
+@Slf4j
+public class AliyunIotSubscribeClient {
+
+    private final PlatformAccount platformAccount;
+
+    public AliyunIotSubscribeClient(PlatformAccount platformAccount) {
+        this.platformAccount = platformAccount;
+        this.accessKey = platformAccount.getAccessKey();
+        this.accessSecret = platformAccount.getAccessSecret();
+        this.regionId = platformAccount.getRegionId();
+        this.consumerGroupId = platformAccount.getConsumerGroupId();
+        this.aliyunUid = platformAccount.getAliyunUid();
+        this.iotInstanceId = platformAccount.getIotInstanceId();
+
+        this.host = aliyunUid+".iot-amqp."+ regionId +".aliyuncs.com";
+        // 设置客户端id
+        setClientIdAndHost();
+    }
+
+    // 设置客户端id
+    private void setClientIdAndHost(){
+        // 获取ip地址
+        InetAddress addr = null;
+        try {
+            addr = InetAddress.getLocalHost();
+        } catch (UnknownHostException e) {
+            e.printStackTrace();
+        }
+        this.clientId = IdWorker.getIdStr();
+    }
+
+    private String accessKey;
+    private String accessSecret;
+    private String consumerGroupId;
+    private String aliyunUid;
+    private String regionId;
+
+    //iotInstanceId:企业版实例请填写实例ID,公共实例请填空字符串""。
+    private String iotInstanceId;
+
+    //控制台服务端订阅中消费组状态页客户端ID一栏将显示clientId参数。
+    //建议使用机器UUID、MAC地址、IP等唯一标识等作为clientId。便于您区分识别不同的客户端。
+    private String clientId;
+
+    //${YourHost}为接入域名,请参见AMQP客户端接入说明文档。
+    private String host;
+
+    // 指定单个进程启动的连接数
+    // 单个连接消费速率有限,请参考使用限制,最大64个连接
+    // 连接数和消费速率及rebalance相关,建议每500QPS增加一个连接
+    private static int connectionCount = 1;
+
+    @Getter
+    private List<Connection> connections = null;
+
+    @Async
+    public void start(MessageListener messageListener) throws Exception {
+        // 先关闭一下
+        close();
+        connections = new ArrayList<>();
+        //参数说明,请参见AMQP客户端接入说明文档。
+        for (int i = 0; i < connectionCount; i++) {
+            long timeStamp = System.currentTimeMillis();
+            //签名方法:支持hmacmd5、hmacsha1和hmacsha256。
+            String signMethod = "hmacsha1";
+
+            //userName组装方法,请参见AMQP客户端接入说明文档。
+            String userName = clientId + "-" + i + "|authMode=aksign"
+                    + ",signMethod=" + signMethod
+                    + ",timestamp=" + timeStamp
+                    + ",authId=" + accessKey
+                    + ",iotInstanceId=" + iotInstanceId
+                    + ",consumerGroupId=" + consumerGroupId
+                    + "|";
+            //计算签名,password组装方法,请参见AMQP客户端接入说明文档。
+            String signContent = "authId=" + accessKey + "&timestamp=" + timeStamp;
+            String password = doSign(signContent, accessSecret, signMethod);
+            String connectionUrl = "failover:(amqps://" + host + ":5671?amqp.idleTimeout=80000)"
+                    + "?failover.reconnectDelay=30";
+
+            Hashtable<String, String> hashtable = new Hashtable<>();
+            hashtable.put("connectionfactory.SBCF", connectionUrl);
+            hashtable.put("queue.QUEUE", "default");
+            hashtable.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.qpid.jms.jndi.JmsInitialContextFactory");
+            Context context = new InitialContext(hashtable);
+            ConnectionFactory cf = (ConnectionFactory)context.lookup("SBCF");
+            Destination queue = (Destination)context.lookup("QUEUE");
+            // 创建连接。
+            Connection connection = cf.createConnection(userName, password);
+            connections.add(connection);
+
+            ((JmsConnection)connection).addConnectionListener(myJmsConnectionListener);
+            // 创建会话。
+            // Session.CLIENT_ACKNOWLEDGE: 收到消息后,需要手动调用message.acknowledge()。
+            // Session.AUTO_ACKNOWLEDGE: SDK自动ACK(推荐)。
+            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
+
+            connection.start();
+            // 创建Receiver连接。
+            MessageConsumer consumer = session.createConsumer(queue);
+            consumer.setMessageListener(messageListener);
+        }
+    }
+
+    public void close() throws Exception {
+        if (connections == null){
+            return;
+        }
+        connections.forEach(c-> {
+            try {
+                c.close();
+            } catch (JMSException e) {
+                log.error("连接关闭失败", e);
+            }
+        });
+    }
+
+
+    private static JmsConnectionListener myJmsConnectionListener = new JmsConnectionListener() {
+        /**
+         * 连接成功建立。
+         */
+        @Override
+        public void onConnectionEstablished(URI remoteURI) {
+            log.info("连接成功, remoteUri:{}", remoteURI);
+        }
+        /**
+         * 尝试过最大重试次数之后,最终连接失败。
+         */
+        @Override
+        public void onConnectionFailure(Throwable error) {
+            log.error("连接失败, {}", error.getMessage());
+        }
+        /**
+         * 连接中断。
+         */
+        @Override
+        public void onConnectionInterrupted(URI remoteURI) {
+            log.info("连接中断, remoteUri:{}", remoteURI);
+        }
+        /**
+         * 连接中断后又自动重连上。
+         */
+        @Override
+        public void onConnectionRestored(URI remoteURI) {
+            log.info("自动重连, remoteUri:{}", remoteURI);
+        }
+        @Override
+        public void onInboundMessage(JmsInboundMessageDispatch envelope) {}
+        @Override
+        public void onSessionClosed(Session session, Throwable cause) {}
+        @Override
+        public void onConsumerClosed(MessageConsumer consumer, Throwable cause) {}
+        @Override
+        public void onProducerClosed(MessageProducer producer, Throwable cause) {}
+    };
+
+    /**
+     * 计算签名,password组装方法,请参见AMQP客户端接入说明文档。
+     */
+    private static String doSign(String toSignString, String secret, String signMethod) throws Exception {
+        SecretKeySpec signingKey = new SecretKeySpec(secret.getBytes(), signMethod);
+        Mac mac = Mac.getInstance(signMethod);
+        mac.init(signingKey);
+        byte[] rawHmac = mac.doFinal(toSignString.getBytes());
+        return Base64.encodeBase64String(rawHmac);
+    }
+
+
+
+}

+ 24 - 0
nb-system/src/main/java/com/nb/aliyun/PlatformAccount.java

@@ -0,0 +1,24 @@
+package com.nb.aliyun;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @Author XX
+ * @Date 2022-01-05 17:33:29
+ * @Version 1.0
+ * @Description XXX
+ */
+@Data
+@ConfigurationProperties(prefix = "aliyun")
+@Configuration
+public class PlatformAccount {
+    private String accessKey = "LTAI4FhB19MgQuviGxwA3aod";
+    private String accessSecret = "cQQVkATR0yv2G9CEtfjAhEGBepPDRs";
+    private String consumerGroupId = "nalavzBm4RuVJc0BUij7000100";
+    private String aliyunUid = "1177450762772738";
+    private String regionId = "cn-shanghai";
+    //iotInstanceId:企业版实例请填写实例ID,公共实例请填空字符串""。
+    private String iotInstanceId = "";
+}

+ 33 - 0
nb-system/src/main/java/com/nb/aliyun/PlatformLog.java

@@ -0,0 +1,33 @@
+package com.nb.aliyun;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @Author XX
+ * @Date 2022-01-10 11:43:57
+ * @Version 1.0
+ * @Description XXX
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class PlatformLog {
+    @TableId(value = "id",type = IdType.AUTO)
+    private Long id;
+    private String code;
+    private String platformCode;
+    private String platformProductCode;
+    private String deviceCode;
+    private String platformData;
+    private String resultData;
+    private String errorLog;
+    private String isError;
+    private Long addTime;
+    private Long updateTime;
+    private String description;
+    private String isDelete;
+}

+ 12 - 0
nb-system/src/main/java/com/nb/aliyun/PlatformType.java

@@ -0,0 +1,12 @@
+package com.nb.aliyun;
+
+/**
+ * @Author XX
+ * @Date 2022-02-12 08:08:15
+ * @Version 1.0
+ * @Description XXX
+ */
+public class PlatformType {
+    public static final String ALIYUN = "10";
+    public static final String TUOREN = "30";
+}

+ 324 - 0
nb-system/src/main/java/com/nb/aliyun/sdk/AliyunIotSdk.java

@@ -0,0 +1,324 @@
+package com.nb.aliyun.sdk;
+
+import com.alibaba.fastjson.JSON;
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.iot.model.v20180120.*;
+import com.aliyuncs.profile.DefaultProfile;
+import com.aliyuncs.profile.IClientProfile;
+import com.nb.aliyun.PlatformAccount;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+//import com.tuoren.common.utils.StringUtils;
+
+/**
+ * @Author 龙三郎
+ * @Date 2021-06-11 08:10:58
+ * @Version 1.0
+ * @Description XXX
+ */
+@Configuration
+@EnableConfigurationProperties(PlatformAccount.class)
+@Slf4j
+public class AliyunIotSdk {
+
+
+    @Value("${aliyun.product.productKey:a1ALlsBa2ZK}")
+    private String productKey;
+
+    private String accessKey;
+    private String accessSecret;
+    private String regionId;
+    private String iotInstanceId;
+    // client
+    private DefaultAcsClient client;
+    public AliyunIotSdk(PlatformAccount platformAccount){
+        this.accessKey = platformAccount.getAccessKey();
+        this.accessSecret = platformAccount.getAccessSecret();
+        this.regionId = platformAccount.getRegionId();
+        this.iotInstanceId = platformAccount.getIotInstanceId();
+    }
+
+    /**
+     * 获取阿里云客户端
+     * @return
+     */
+    protected final DefaultAcsClient getAliyuniotClient(){
+        if (client != null){
+            return client;
+        }
+        IClientProfile profile = DefaultProfile.getProfile(regionId, accessKey, accessSecret);
+        DefaultAcsClient client = new DefaultAcsClient(profile); //初始化SDK客户端。
+        this.client = client;
+        return client;
+    }
+
+    /**
+     * 	查询产品列表。
+     * @return
+     */
+    public List<QueryProductListResponse.Data.ProductInfo> queryProductList(){
+        // 获取阿里云SDK客户端
+        DefaultAcsClient client = this.getAliyuniotClient();
+        List<QueryProductListResponse.Data.ProductInfo> products = new ArrayList<>();
+        Integer pageSize = 100,currentPage = 1;
+        while (true){
+            QueryProductListRequest queryProductListRequest = new QueryProductListRequest();
+            queryProductListRequest.setPageSize(pageSize);
+            queryProductListRequest.setCurrentPage(currentPage);
+            try {
+                QueryProductListResponse queryProductListResponse = client.getAcsResponse(queryProductListRequest);
+                System.out.println(queryProductListResponse.getSuccess());
+                // 获取失败直接跳出循环
+                if (!queryProductListResponse.getSuccess()){
+                    break;
+                }
+                QueryProductListResponse.Data data = queryProductListResponse.getData();
+                // 添加到列表
+                products.addAll(data.getList());
+                // 判断是否获取了全部
+                if (data.getCurrentPage() < data.getPageCount()){
+                    // 当前页小于总页数,说明没有获取完
+                    currentPage++;
+                }else {
+                    break;
+                }
+            } catch (ClientException e) {
+                e.printStackTrace();
+            }
+        }
+        return products;
+    }
+
+
+    /**
+     * 注册设备
+     * @param
+     * @return
+     */
+    public RegisterDeviceResponse.Data registerDevice(String productKey, String name){
+
+        // 获取阿里云SDK客户端
+        DefaultAcsClient client = this.getAliyuniotClient();
+        List<QueryDeviceResponse.DeviceInfo> list = new ArrayList<>();
+        Integer pageSize = 100,currentPage = 1;
+
+        RegisterDeviceRequest request = new RegisterDeviceRequest();
+        // 产品key,必需
+        request.setProductKey(productKey);
+        // 产品名称,非必需
+        request.setDeviceName(name);
+        try {
+            RegisterDeviceResponse response = client.getAcsResponse(request);
+            System.out.println(response.getSuccess());
+            // 获取失败直接跳出循环
+            if (!response.getSuccess()){
+                return null;
+            }
+            RegisterDeviceResponse.Data data = response.getData();
+            return data;
+        } catch (ClientException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * 删除设备
+     * @param
+     * @return
+     */
+    public int deleteDevice(String iotId){
+        // 获取阿里云SDK客户端
+        DefaultAcsClient client = this.getAliyuniotClient();
+        DeleteDeviceRequest request = new DeleteDeviceRequest();
+        request.setIotInstanceId(iotInstanceId);
+        // 产品iotId,必需
+        request.setIotId(iotId);
+        try {
+            DeleteDeviceResponse response = client.getAcsResponse(request);
+            // 获取失败直接跳出循环
+            if (!response.getSuccess()){
+                return 1;
+            }
+        } catch (ClientException e) {
+            e.printStackTrace();
+        }
+        return 0;
+    }
+
+    /**
+     * 获取设备
+     * @return
+     */
+    public List<QueryDeviceResponse.DeviceInfo> queryDevice(){
+        long startTime = System.currentTimeMillis();
+
+        // 获取阿里云SDK客户端
+        DefaultAcsClient client = this.getAliyuniotClient();
+        List<QueryDeviceResponse.DeviceInfo> list = new ArrayList<>();
+        Integer pageSize = 100,currentPage = 1;
+        while (true){
+            QueryDeviceRequest request = new QueryDeviceRequest();
+            request.setPageSize(pageSize);
+            request.setCurrentPage(currentPage);
+            request.setProductKey(productKey);
+            request.setIotInstanceId(iotInstanceId);
+            try {
+                QueryDeviceResponse response = client.getAcsResponse(request);
+                System.out.println(response.getSuccess());
+                // 获取失败直接跳出循环
+                if (!response.getSuccess()){
+                    break;
+                }
+                List<QueryDeviceResponse.DeviceInfo> data = response.getData();
+                // 添加到列表
+                list.addAll(data);
+                // 判断是否获取了全部
+                if (response.getPage() < response.getPageCount()){
+                    // 当前页小于总页数,说明没有获取完
+                    currentPage++;
+                }else {
+                    break;
+                }
+            } catch (ClientException e) {
+                e.printStackTrace();
+            }
+        }
+        log.info("从阿里云拉取设备信息耗时【{}】",System.currentTimeMillis()-startTime);
+        return list;
+    }
+
+
+    /**
+     * 获取阿里云消费组
+     * @return
+     */
+    public List<QueryConsumerGroupListResponse.ConsumerGroupDTO> queryConsumerGroupList(){
+
+        // 获取阿里云SDK客户端
+        DefaultAcsClient client = this.getAliyuniotClient();
+
+        QueryConsumerGroupListRequest _request = new QueryConsumerGroupListRequest();
+        _request.setPageSize(100);
+        _request.setCurrentPage(1);
+
+        List<QueryConsumerGroupListResponse.ConsumerGroupDTO> list = null;
+
+        try {
+            QueryConsumerGroupListResponse _response = client.getAcsResponse(_request);
+            list = _response.getData();
+        } catch (ClientException e) {
+            e.printStackTrace();
+        }
+
+        return list;
+
+    }
+
+    /**
+     * 创建一个消费组
+     * @param groupName
+     * @return
+     */
+    public CreateConsumerGroupResponse createConsumerGroup(String groupName){
+        // 获取阿里云SDK客户端
+        DefaultAcsClient client = this.getAliyuniotClient();
+
+        CreateConsumerGroupRequest _request = new CreateConsumerGroupRequest();
+        _request.setGroupName(groupName);
+
+        CreateConsumerGroupResponse acsResponse = null;
+        try {
+            acsResponse = client.getAcsResponse(_request);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return acsResponse;
+    }
+
+    /**
+     * 创建一个云产品流转规则
+     * @param ruleName
+     * @param productKey
+     * @param device_list
+     * @return
+     */
+    public CreateRuleResponse createRule(String ruleName, String productKey, String[] device_list){
+        // 获取阿里云SDK客户端
+        DefaultAcsClient client = this.getAliyuniotClient();
+
+        CreateRuleRequest _request = new CreateRuleRequest();
+        _request.setName(ruleName);
+        _request.setSelect("*");
+        _request.setShortTopic("+/thing/event/property/post");
+        _request.setProductKey(productKey);
+//        _request.setWhere("deviceName() in ("+ StringUtils.arrayToString(device_list,"'",",") +")");
+
+
+        CreateRuleResponse acsResponse = null;
+        try {
+            acsResponse = client.getAcsResponse(_request);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return acsResponse;
+    }
+
+    public CreateRuleActionResponse createRuleAction(Long ruleId, String groupId){
+        // 获取阿里云SDK客户端
+        DefaultAcsClient client = this.getAliyuniotClient();
+
+        CreateRuleActionRequest _request = new CreateRuleActionRequest();
+        _request.setRuleId(ruleId);
+        _request.setType("AMQP");
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("groupId",groupId);
+
+        _request.setConfiguration(JSON.toJSONString(map));
+
+
+        CreateRuleActionResponse acsResponse = null;
+        try {
+            acsResponse = client.getAcsResponse(_request);
+        }catch (Exception e){
+            e.printStackTrace();
+        }
+        return acsResponse;
+    }
+
+
+
+    /**
+     * 获取设备的详细信息
+     * @param deviceName
+     */
+    public QueryDeviceDetailResponse queryDeviceDetail(String deviceName){
+        // 获取阿里云SDK客户端
+        DefaultAcsClient client = this.getAliyuniotClient();
+
+        QueryDeviceDetailRequest _request = new QueryDeviceDetailRequest();
+        _request.setProductKey(productKey);
+        _request.setDeviceName(deviceName);
+        _request.setIotInstanceId(iotInstanceId);
+
+        QueryDeviceDetailResponse acsResponse = null;
+        try {
+            acsResponse = client.getAcsResponse(_request);
+        } catch (ClientException e) {
+            e.printStackTrace();
+        }
+        return acsResponse;
+    }
+
+
+}

+ 16 - 0
nb-system/src/main/java/com/nb/aliyun/utils/Constants.java

@@ -0,0 +1,16 @@
+package com.nb.aliyun.utils;
+
+/**
+ * @Author 龙三郎
+ * @Date 2022-04-26 13:44:36
+ * @Version 1.0
+ * @Description XXX
+ */
+public class Constants {
+    // 默认医院
+    public static final String DefaultHospital = "1";
+    // 默认创建人
+    public static final String DefaultCreateBy = "1";
+    // 默认更新人
+    public static final String DefaultUpdateBy = "1";
+}

+ 48 - 0
nb-system/src/main/java/com/nb/aliyun/utils/DeviceAlarmUtils.java

@@ -0,0 +1,48 @@
+package com.nb.aliyun.utils;
+
+import com.nb.bus.enums.DeviceAlarmEnum;
+
+/**
+ * @Author 龙三郎
+ * @Date 2022-04-29 11:04:04
+ * @Version 1.0
+ * @Description 根据阿里云发送的报警标识,获取报警枚举对象。阿里云发送的报警是0-9的整数。
+ */
+public class DeviceAlarmUtils {
+    public static DeviceAlarmEnum getAlarm(Integer i){
+        if (i == 0){
+            // 无报警
+            return DeviceAlarmEnum.None;
+        }else if (i == 1){
+            // 气泡无液报警
+            return DeviceAlarmEnum.Bubble;
+        }else if (i == 2){
+            // 堵塞
+            return DeviceAlarmEnum.Jam;
+        }else if (i == 3){
+            // 输入总量
+            return DeviceAlarmEnum.InfusionMax;
+        }else if (i == 4){
+            // 极限量
+            return DeviceAlarmEnum.Limit;
+        }else if (i == 5){
+            // 电量耗尽
+            return DeviceAlarmEnum.LowBattery;
+        }else if (i == 6){
+            // 输液结束
+            return DeviceAlarmEnum.Finished;
+        }else if (i == 7){
+            // 电机失控
+            return DeviceAlarmEnum.OutOfControl;
+        }else if (i == 8){
+            // 机械故障
+            return DeviceAlarmEnum.Machine;
+        }else if (i == 9){
+            // 未装药盒
+            return DeviceAlarmEnum.NotBox;
+        }else {
+
+            return DeviceAlarmEnum.Finished;
+        }
+    }
+}

+ 32 - 0
nb-system/src/main/java/com/nb/aliyun/utils/DeviceRunStatusUtils.java

@@ -0,0 +1,32 @@
+package com.nb.aliyun.utils;
+
+import com.nb.bus.enums.DeviceStatusEnum;
+
+/**
+ * @Author 龙三郎
+ * @Date 2022-04-29 11:36:03
+ * @Version 1.0
+ * @Description 根据阿里云发送的运行标识,获取运行枚举对象。阿里云发送的运行是0-4的整数。
+ */
+public class DeviceRunStatusUtils {
+    public static DeviceStatusEnum getRunStatus(Integer i){
+        if (i == 0){
+            // 关机
+            return DeviceStatusEnum.Shutdown;
+        }else if (i == 1){
+            // 运行
+            return DeviceStatusEnum.StartUp;
+        }else if (i == 2){
+            // 运行
+            return DeviceStatusEnum.Running;
+        }else if (i == 3){
+            // 暂停
+            return DeviceStatusEnum.Pause;
+        }else if (i == 4){
+            // 待机
+            return DeviceStatusEnum.Waiting;
+        }else {
+            return DeviceStatusEnum.Shutdown;
+        }
+    }
+}

+ 25 - 0
nb-system/src/main/java/com/nb/aliyun/utils/DeviceTypeUtils.java

@@ -0,0 +1,25 @@
+package com.nb.aliyun.utils;
+
+import com.nb.bus.enums.DeviceTypeEnum;
+
+/**
+ * @Author 龙三郎
+ * @Date 2022-04-29 11:52:36
+ * @Version 1.0
+ * @Description XXX
+ */
+public class DeviceTypeUtils {
+    public static DeviceTypeEnum getDeviceType(Integer i){
+        if (i == 0){
+            return DeviceTypeEnum.no;
+        }else if (i == 1){
+            return DeviceTypeEnum.continuous;
+        }else if (i == 2){
+            return DeviceTypeEnum.pulse;
+        }else if (i == 3){
+            return DeviceTypeEnum.intelligent;
+        }else {
+            return DeviceTypeEnum.other;
+        }
+    }
+}

+ 29 - 0
nb-system/src/main/java/com/nb/aliyun/utils/EnumUtils.java

@@ -0,0 +1,29 @@
+package com.nb.aliyun.utils;
+
+import com.nb.bus.enums.DeviceStatusEnum2;
+
+/**
+ * @Author 龙三郎
+ * @Date 2022-05-06 15:03:48
+ * @Version 1.0
+ * @Description XXX
+ */
+public class EnumUtils {
+    /**
+     * @author 龙三郎
+     * 根据阿里云返回的状态获取枚举
+     * @param status
+     * @return
+     */
+    public static DeviceStatusEnum2 getDeviceStatusEnum2(String status){
+        status = status.toLowerCase();
+        if ("online".equals(status)){
+            return DeviceStatusEnum2.ONLINE;
+        }else if ("offline".equals(status)){
+            return DeviceStatusEnum2.OFFLINE;
+        }else{
+            return DeviceStatusEnum2.NOACTIVE;
+        }
+    }
+
+}

+ 43 - 0
nb-system/src/main/java/com/nb/aliyun/utils/Items.java

@@ -0,0 +1,43 @@
+package com.nb.aliyun.utils;
+
+import com.alibaba.fastjson.JSONObject;
+import lombok.AllArgsConstructor;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * @Author 龙三郎
+ * @Date 2022-04-26 10:18:08
+ * @Version 1.0
+ * @Description 方便获取阿里云数据中的items中的属性的value
+ */
+@AllArgsConstructor
+public class Items {
+    public static final String VALUE = "value";
+    public static final String TIME = "time";
+
+    private JSONObject items;
+
+    public String getString(PumpParams param){
+        return items.getJSONObject(param.getParam()).getString(VALUE);
+    }
+
+    public Integer getInteger(PumpParams param){
+        return items.getJSONObject(param.getParam()).getInteger(VALUE);
+    }
+
+    public Date getDate(){
+        long time = items.getJSONObject(PumpParams.patientCode.getParam()).getLong(TIME);
+        return new Date(time);
+    }
+
+    public BigDecimal getBigDecimal(PumpParams param){
+        return items.getJSONObject(param.getParam()).getBigDecimal(VALUE);
+    }
+
+    public Boolean getBoolean(PumpParams param){
+        return items.getJSONObject(param.getParam()).getBoolean(VALUE);
+    }
+
+}

+ 67 - 0
nb-system/src/main/java/com/nb/aliyun/utils/PumpParams.java

@@ -0,0 +1,67 @@
+package com.nb.aliyun.utils;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @Author 龙三郎
+ * @Date 2022-04-26 10:37:47
+ * @Version 1.0
+ * @Description 设备上传的数据参数
+ */
+@AllArgsConstructor
+public enum PumpParams {
+    protocolHeader("protocolHeader","协议头"),
+    dataLength("dataLength","数据长度"),
+    pumpType("pumpType","泵类型"),
+    classification("classification","分类标识"),
+    dataNumber("dataNumber","数据编号"),
+    patientCodeLength("patientCodeLength","住院号长度"),
+    patientCode("patientCode","住院号"),
+    ward("ward","病区"),
+    bedNo("bedNo","床号"),
+    totalDose("totalDose","总量"),
+    maxDose("maxDose","极限量"),
+    singleDosis("singleDose","追加量"),
+    lockTime("lockTime","锁时时间"),
+    flow("flow","持续量"),
+    finishedDose("finishedDose","已输入量"),
+    firstDosis("firstDose","首次量"),
+    pcaValid("pcaValid","PCA有效次数"),
+    pcaInvalid("pcaInvalid","PCA无效次数"),
+    electricQuantity("electricQuantity","电池电量"),
+    runStatus("runStatus","运行状态"),
+    alarmStatus("alarmStatus","报警"),
+    warnWillFinished("warnWillFinished","输液将结束预报"),
+    warnAnalgesicPoor("warnAnalgesicPoor","镇痛不足预报"),
+    warnLowBattery("warnLowBattery","电量偏低预报"),
+
+    // 脉冲泵属性
+    pulseLockTime("pulseLockTime","脉冲量锁时"),
+    firstLockTime("firstLockTime","首次锁时"),
+    pulseDose("pulseDose","脉冲量"),
+
+    // 智能泵属性
+    resetParams("resetParams","重设参数标志"),
+    warnFlow("warnFlow","加减档预报"),
+    flowDownTime("flowDownTime","减档时间"),
+    flowUpTime("flowUpTime","加档时间"),
+    flowUpPcaValid("flowUpPcaValid","触发加档pca有效次数"),
+    flowAdjuseRate("flowAdjuseRate","加减档百分比"),
+    minFlow("minFlow","减档下限"),
+    maxFlow("maxFlow","加档上限"),
+
+
+
+
+
+    CRC("CRC","CRC");
+
+
+
+    @Getter
+    private String param;
+    @Getter
+    private String info;
+
+}

+ 36 - 0
nb-system/src/main/java/com/nb/aliyun/utils/WarnFlowUtils.java

@@ -0,0 +1,36 @@
+package com.nb.aliyun.utils;
+
+import com.nb.bus.enums.FlowStatusEnum;
+
+/**
+ * @Author 龙三郎
+ * @Date 2022-04-29 11:04:04
+ * @Version 1.0
+ * @Description 根据阿里云发送的加减档预报。阿里云发送的报警是0-5的整数。
+ */
+public class WarnFlowUtils {
+    public static FlowStatusEnum getAlarm(Integer i){
+        if (i == 0){
+            // 正常
+            return FlowStatusEnum.None;
+        }else if (i == 1){
+            // 加档受限
+            return FlowStatusEnum.Limited;
+        }else if (i == 2){
+            // 流速已达上限
+            return FlowStatusEnum.MaxFlow;
+        }else if (i == 3){
+            // 加档
+            return FlowStatusEnum.Up;
+        }else if (i == 4){
+            // 减档
+            return FlowStatusEnum.Down;
+        }else if (i == 5){
+            // 低输注状态
+            return FlowStatusEnum.Lowest;
+        }else {
+
+            return FlowStatusEnum.None;
+        }
+    }
+}

+ 18 - 0
nb-system/src/main/java/com/nb/bus/bean/AliIotConfig.java

@@ -0,0 +1,18 @@
+package com.nb.bus.bean;
+
+import lombok.Data;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AliIotConfig.java
+ * @Description 阿里云物联网注册设备返回配置结果
+ * @createTime 2022年04月01日 15:43:00
+ */
+@Data
+public class AliIotConfig {
+    private String deviceName;
+    private String deviceSecret;
+    private String productKey;
+    private String iotId;
+}

+ 15 - 0
nb-system/src/main/java/com/nb/bus/bean/GeoPoint.java

@@ -0,0 +1,15 @@
+package com.nb.bus.bean;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class  GeoPoint implements Serializable {
+    private String lon;
+    private String lat;
+}

+ 24 - 0
nb-system/src/main/java/com/nb/bus/bean/Script.java

@@ -0,0 +1,24 @@
+package com.nb.bus.bean;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName Script.java
+ * @Description TODO
+ * @createTime 2022年03月28日 14:19:00
+ */
+@Data
+public class Script implements Serializable {
+    @JsonIgnore
+    private String id="python";
+    private String content;
+    /**
+     * json xml soap
+     */
+    private String type;
+}

+ 16 - 0
nb-system/src/main/java/com/nb/bus/constant/ClinicConstant.java

@@ -0,0 +1,16 @@
+package com.nb.bus.constant;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName ClinicConstant.java
+ * @Description 临床常量
+ * @createTime 2022年04月13日 15:07:00
+ */
+public class ClinicConstant {
+
+    /**
+     * 当临床不存在时,采用该常量作为临时的临床id,当临床信息从his更新后,在进行替换
+     */
+    public static final String TEMP_ID="-1";
+}

+ 57 - 0
nb-system/src/main/java/com/nb/bus/controller/BusAlarmController.java

@@ -0,0 +1,57 @@
+package com.nb.bus.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.core.mapper.Mapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.nb.bus.controller.vo.CauseVo;
+import com.nb.bus.service.dto.AlarmQuery;
+import com.nb.bus.entity.BusDeviceAlarmEntity;
+import com.nb.bus.service.LocalBusDeviceAlarmService;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseCrudController;
+import com.nb.common.result.R;
+import io.swagger.annotations.*;
+import lombok.AllArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusAlarmController.java
+ * @Description TODO
+ * @createTime 2022年04月08日 10:20:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/alarm")
+@Api(tags = "设备报警管理",value = "统一权限前缀(bus:alarm),例如新增bus:alarm:add")
+public class BusAlarmController extends BaseCrudController<BusDeviceAlarmEntity, String> {
+
+    private final LocalBusDeviceAlarmService deviceAlarmService;
+
+    @PostMapping("/cause/{id}")
+    @ApiOperation(value = "添加报警原因",notes = "权限:【bus:alarm:cause】")
+    @SaCheckPermission("bus:alarm:cause")
+    public R<Boolean> cause(@PathVariable("id") String id, @RequestBody@Validated CauseVo cause) {
+        deviceAlarmService.addCause(id,cause.getCause());
+        return R.success(true);
+    }
+
+
+    @PostMapping("/query/page")
+    @SaCheckPermission("bus:alarm:query")
+    @ApiOperation(value = "分页(输注查询)查询",notes = "权限:【bus:alarm:query】")
+    public R<IPage<BusDeviceAlarmEntity>> page(@RequestBody@Validated AlarmQuery query){
+        return R.success(deviceAlarmService.pageQuery(query));
+    }
+    @Override
+    public BaseService<? extends Mapper<BusDeviceAlarmEntity>, BusDeviceAlarmEntity, String> getService() {
+        return deviceAlarmService;
+    }
+
+    @Override
+    public String getPermissionPrefix() {
+        return "bus:alarm";
+    }
+}

+ 72 - 0
nb-system/src/main/java/com/nb/bus/controller/BusAppController.java

@@ -0,0 +1,72 @@
+package com.nb.bus.controller;
+
+import cn.dev33.satoken.SaManager;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.stp.StpLogic;
+import cn.hutool.core.text.CharSequenceUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.Mapper;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseQueryController;
+import com.nb.common.crud.controller.BaseSaveController;
+import com.nb.common.result.R;
+import com.nb.bus.entity.BusAppConfig;
+import com.nb.bus.service.LocalBusAppConfigService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName SysAppController.java
+ * @Description TODO
+ * @createTime 2022年06月24日 15:02:00
+ */
+@RestController
+@RequestMapping("/bus/app")
+@Api(tags = "app安装包管理",description="app安装包管理")
+public class BusAppController implements BaseQueryController<BusAppConfig,String>, BaseSaveController<BusAppConfig,String> {
+    @Resource
+    private LocalBusAppConfigService appConfigService;
+
+
+    @GetMapping("/last")
+    @ApiOperation(value="查看最新的安装包信息")
+    public R get() {
+        return R.success(appConfigService.getOne(new QueryWrapper<BusAppConfig>().lambda().last("limit 1")));
+    }
+
+
+
+    @PostMapping
+    @SaCheckPermission("bus:app:save")
+    @ApiOperation(value="上传安装包",notes = "权限【bus:app:save】")
+    public R save(@RequestBody BusAppConfig busAppConfig) {
+        if(CharSequenceUtil.isAllBlank(busAppConfig.getId())){
+            BusAppConfig exist = appConfigService.getOne(new QueryWrapper<BusAppConfig>().lambda().last("limit 1"));
+            if(exist!=null){
+                busAppConfig.setId(exist.getId());
+            }
+        }
+        appConfigService.saveOrUpdate(busAppConfig);
+        return R.success();
+    }
+
+    @Override
+    public BaseService<? extends Mapper<BusAppConfig>, BusAppConfig, String> getService() {
+        return appConfigService;
+    }
+
+    @Override
+    public String getPermissionPrefix() {
+        return "bus:app";
+    }
+
+    @Override
+    public StpLogic getStpLogin() {
+        return SaManager.getStpLogic("");
+    }
+}

+ 207 - 0
nb-system/src/main/java/com/nb/bus/controller/BusClinicController.java

@@ -0,0 +1,207 @@
+package com.nb.bus.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.nb.bus.controller.vo.ClinicStatsVo;
+import com.nb.bus.controller.vo.ClinicEditVo;
+import com.nb.bus.entity.*;
+import com.nb.bus.service.*;
+import com.nb.bus.service.dto.*;
+import com.nb.common.annotation.Log;
+import com.nb.common.entity.GenericEntity;
+import com.nb.common.exception.CustomException;
+import com.nb.common.result.R;
+import io.swagger.annotations.*;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalController.java
+ * @Description
+ * @createTime 2022年03月19日 09:28:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/clinic")
+@Slf4j
+@Api(tags = "病人临床手术管理",description = "统一权限前缀(bus:clinic),例如新增bus:clinic:add")
+public class BusClinicController {
+    private final LocalBusClinicService clinicService;
+    private final LocalBusEvaluationService evaluationService;
+    private final LocalBusInfusionHistoryService infusionHistoryService;
+    private final LocalBusInfusionModifyService infusionModifyService;
+    private final LocalBusDeviceHistoryService deviceHistoryService;
+    private final LocalBusDeviceManualService deviceManualService;
+    private final LocalBusPatientService patientService;
+    private final LocalBusHospitalService hospitalService;
+    private final LocalBusDeviceService deviceService;
+    @PostMapping("/stats")
+    @ApiOperation(value = "临床过程中的数据记录",notes = "权限【无】")
+    public R<ClinicStatsReturnResult> stats(@RequestBody@Validated ClinicStatsVo statsVo) {
+        if (!(statsVo.isAppendDose()||statsVo.isContinueDose()||statsVo.isInputDose()||statsVo.isInValidCount()||statsVo.isValidCount())){
+            return R.success(new ClinicStatsReturnResult());
+        }
+        if(statsVo.isStatsByModify()&&CollUtil.isEmpty(statsVo.getInfusionModifyIds())){
+            return R.success(new ClinicStatsReturnResult());
+        }
+        return R.success(clinicService.stats(statsVo));
+    }
+
+
+    @PostMapping("/page")
+    @SaCheckPermission("bus:clinic:query")
+    @ApiOperation(value = "临床管理分页查询",notes = "权限【bus:clinic:query】")
+    public R<IPage<ClinicResult>> page(@RequestBody@Validated ClinicQuery query) {
+        return R.success(clinicService.pageQuery(query));
+    }
+
+    @PostMapping("/latest/page")
+    @SaCheckPermission("bus:clinic:query")
+    @ApiOperation(value = "病人当前临床管理分页查询",notes = "权限【bus:clinic:query】")
+    public R<IPage<ClinicResult>> latestPage(@RequestBody@Validated ClinicQuery query) {
+        return R.success(clinicService.latestQueryPage(query));
+    }
+
+    @Log(title = "查询临床信息的镇痛访视记录单")
+    @PostMapping("/anal/record/{clinicId}/{after}")
+    @SaCheckPermission("bus:clinic:query")
+    @ApiOperation(value = "查询临床信息的镇痛访视记录单",notes = "权限【bus:clinic:query】")
+    public R<ClinicAnalRecordResult> analRecord(@PathVariable("clinicId") String clinicId,@PathVariable("after")@ApiParam("0、术前 1、术后") Boolean after,@RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId){
+        log.info("查询临床访视单【{}】",clinicId);
+        ClinicAnalRecordResult result = new ClinicAnalRecordResult();
+        BusClinicEntity clinic = clinicService.getById(clinicId);
+        if(clinic==null){
+            throw new CustomException("该临床信息不存在");
+        }
+        ClinicAnalClinicRecord record = ClinicAnalClinicRecord.parse(clinic);
+        record.setTenantName(hospitalService.getName(tenantId));
+        //填充临床信息
+        result.setClinic(record);
+        if(Boolean.TRUE.equals(after)){
+            //填充评价记录
+            result.setEvaluations(evaluationService.list(new QueryWrapper<BusEvaluationEntity>().lambda().eq(BusEvaluationEntity::getClinicId,clinicId).orderByAsc(BusEvaluationEntity::getEvaluateTime)));
+
+            if(Boolean.TRUE.equals(clinic.getMonitorType())){
+                //填充输注记录
+                List<BusInfusionHistoryEntity> infusionHistories = infusionHistoryService.list(new QueryWrapper<BusInfusionHistoryEntity>().lambda()
+                        .eq(BusInfusionHistoryEntity::getClinicId, clinicId)
+                        .orderByAsc(BusInfusionHistoryEntity::getStartTime));
+                log.debug("《《《《《《《《《《《《《《《《《填充输注记录结束》》》》》》》》》》》》》》》》");
+                if(CollUtil.isNotEmpty(infusionHistories)){
+                    result.fillUndoInfo(infusionHistories);
+                    //添加设备别名
+                    Map<String, List<BusInfusionHistoryEntity>> infusionByDeviceId = infusionHistories.stream().collect(Collectors.groupingBy(BusInfusionHistoryEntity::getDeviceId));
+                    if(CollUtil.isNotEmpty(infusionByDeviceId)){
+                        Set<String> deviceIds = infusionByDeviceId.keySet();
+                        List<BusDeviceEntity> devices
+                                = deviceService.list(new QueryWrapper<BusDeviceEntity>().lambda().select(BusDeviceEntity::getId, BusDeviceEntity::getDeviceId, BusDeviceEntity::getAlias).in(BusDeviceEntity::getId, deviceIds));
+                        Map<String, List<BusDeviceEntity>> aliasByDeviceId = devices.stream().collect(Collectors.groupingBy(BusDeviceEntity::getDeviceId));
+                        if(CollUtil.isNotEmpty(aliasByDeviceId)){
+                            infusionByDeviceId.forEach((deviceId,infusions)->{
+                                aliasByDeviceId.computeIfPresent(deviceId,(k,alias)->{
+                                    infusions.forEach(infusion->infusion.setAlias(alias.get(0).getAlias()));
+                                    return alias;
+                                });
+                            });
+                        }
+                    }
+                    log.debug("《《《《《《《《《《《《《《《《《添加设备别名结束》》》》》》》》》》》》》》》》");
+                    List<String> infusionIds = infusionHistories.stream().map(BusInfusionHistoryEntity::getId).collect(Collectors.toList());
+                    List<BusInfusionModifyEntity> infusionModifies = infusionModifyService.list(new QueryWrapper<BusInfusionModifyEntity>().lambda()
+                            .in(BusInfusionModifyEntity::getInfusionId, infusionIds));
+                    result.fillInfusionRecords(infusionHistories,infusionModifies);
+
+                    List<BusDeviceHistoryEntity> deviceHistories = deviceHistoryService.list(new QueryWrapper<BusDeviceHistoryEntity>()
+                            .lambda()
+                            .select(BusDeviceHistoryEntity::getInfusionId,BusDeviceHistoryEntity::getPcaValidCount,BusDeviceHistoryEntity::getInfusionModifyId,
+                                    BusDeviceHistoryEntity::getPcaInvalidCount,BusDeviceHistoryEntity::getInputDose,BusDeviceHistoryEntity::getUploadTime)
+                            .in(BusDeviceHistoryEntity::getInfusionId, infusionIds)
+                            .eq(BusDeviceHistoryEntity::getMaster, true));
+                    log.debug("《《《《《《《《《《《《《《《《《查询设备历史结束》》》》》》》》》》》》》》》》");
+                    //标记输注
+                    result.markInfusion(deviceHistories);
+                    List<ClinicStatsQueryResult> statsQueryResults = deviceHistories.stream().map(history -> {
+                        ClinicStatsQueryResult statsQueryResult = new ClinicStatsQueryResult();
+                        statsQueryResult.setInputDose(history.getInputDose());
+                        statsQueryResult.setInValidCount(history.getPcaInvalidCount());
+                        statsQueryResult.setValidCount(history.getPcaValidCount());
+                        statsQueryResult.setUploadTime(history.getUploadTime());
+                        return statsQueryResult;
+                    }).collect(Collectors.toList());
+                    //填充统计信息
+                    result.setStats(ClinicStatsReturnResult.of(statsQueryResults));
+                    log.debug("《《《《《《《《《《《《《《《《《填充统计信息结束》》》》》》》》》》》》》》》》");
+
+                }
+            }
+        }
+
+        return R.success(result);
+    }
+
+
+    @Log(title = "编辑病人(当前)信息")
+    @PostMapping("/edit")
+    @SaCheckPermission("bus:clinic:edit")
+    @ApiOperation(value = "编辑病人(当前)信息",notes = "编辑病人临床信息,权限【bus:clinic:edit】")
+    public R<Boolean> edit(@RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId, @RequestBody ClinicEditVo entity){
+        if(entity.getHaveDevice()){
+            BusClinicEntity clinic = entity.getClinic();
+            if(clinic==null){
+                throw new CustomException("临床信息不能为空");
+            }
+            BusPatientEntity patient = patientService.getOne(new QueryWrapper<BusPatientEntity>().lambda().eq(BusPatientEntity::getCode,clinic.getPatientCode()));
+            //仅更新最新的临床数据
+            clinic.setId(patient.getClinicId());
+            clinic.setTenantId(tenantId);
+            patientService.manualEdit(clinic);
+            return R.success(true);
+        }else {
+            deviceManualService.edit(entity.getManual(),entity.getClinic());
+            return R.success(true);
+        }
+    }
+
+    @Log(title = "新增无泵监控手术信息")
+    @PostMapping("/save")
+    @SaCheckPermission("bus:clinic:save")
+    @ApiOperation(value = "新增手术信息",notes = "新增病人信息,权限【bus:clinic:save】")
+    public R save(@RequestBody ClinicEditVo entity){
+        deviceManualService.save(entity.getManual(),entity.getClinic());
+        return R.success();
+    }
+
+    @Log(title = "查询病人信息详情")
+    @PostMapping("/view/{id}")
+    @SaCheckPermission("bus:clinic:query")
+    @ApiOperation(value = "根据ID查询",notes = "新增病人信息,权限【bus:clinic:query】")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id",value = "主键id",required = true)
+    })
+    public R<BusClinicEntity> getById(@PathVariable("id") String id) {
+        String key=String.valueOf(id);
+        if(StrUtil.isNullOrUndefined(key)){
+            return R.fail("查询结果不存在");
+        }
+        return R.success(clinicService.getById(key));
+    }
+
+    @PostMapping("/edit/any")
+    @SaCheckPermission("bus:clinic:edit")
+    @ApiOperation(value = "根据ID修改数据(所有临床数据)",notes = "新增病人信息,权限【bus:clinic:edit】")
+    public R update(@RequestBody @Validated(GenericEntity.Update.class)BusClinicEntity payload){
+        return clinicService.updateById(payload)?R.success(payload):R.fail("更新失败");
+    }
+
+}

+ 116 - 0
nb-system/src/main/java/com/nb/bus/controller/BusConstantController.java

@@ -0,0 +1,116 @@
+package com.nb.bus.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.nb.bus.enums.ConstantEnum;
+import com.nb.bus.service.constant.AbstractConstantService;
+import com.nb.common.entity.QueryParamEntity;
+import com.nb.common.exception.CustomException;
+import com.nb.common.result.R;
+import io.swagger.annotations.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.Serializable;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusConstantController.java
+ * @Description
+ * @createTime 2022年04月08日 10:19:00
+ */
+@RestController
+@RequestMapping("/bus/constant")
+@Api(tags = "常量管理",value = "统一权限前缀(bus:constant),例如新增bus:constant:add")
+public class BusConstantController {
+
+    private HashMap<ConstantEnum, AbstractConstantService> constantHashMap=new HashMap<>();
+
+
+    @Autowired
+    public BusConstantController(List<AbstractConstantService> constantList) {
+        constantList.stream().collect(Collectors.groupingBy(AbstractConstantService::getName)).forEach((k, vs)->{
+            constantHashMap.merge(k,vs.get(0),(o1,o2)->{
+                throw new RuntimeException("常量服务类名称不可重复[{"+o1.getName()+"}]");
+            });
+        });
+    }
+
+    @PostMapping("/{type}/page")
+    @SaCheckPermission("bus:constant:query")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type",value = "常量类型",required = true,allowableValues = "mix,doctor,alarm",allowMultiple = true,dataTypeClass = ConstantEnum.class),
+            @ApiImplicitParam(name = "query",value = "查询参数",required = true)
+    })
+    @ApiOperation(value = "使用POST方式分页动态查询",notes = "权限【bus:constant:query】")
+    public R<IPage<?>> queryPager(@PathVariable("type") ConstantEnum type,@RequestBody QueryParamEntity<?> query) {
+        AbstractConstantService constantService = constantHashMap.get(type);
+        if(constantService==null){
+            return R.fail("常量类型{"+type+"}不存在");
+        }
+        return R.success(constantService.list(query));
+    }
+
+    @PostMapping("/{type}/remove")
+    @SaCheckPermission("bus:constant:delete")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id",value = "主键id",required = true),
+            @ApiImplicitParam(name = "type",value = "常量类型",required = true,allowableValues = "mix,doctor,alarm",allowMultiple = true,dataTypeClass = ConstantEnum.class)
+    })
+    @ApiOperation(value = "根据ID删除",notes = "权限【bus:constant:delete】")
+    public R delete(@PathVariable("type") ConstantEnum type,@RequestParam("id") Serializable id) {
+        if(StrUtil.isNullOrUndefined(String.valueOf(id))){
+            return R.success();
+        }
+        AbstractConstantService constantService = constantHashMap.get(type);
+        if(constantService==null){
+            return R.success("常量类型{"+type+"}不存在");
+        }
+        String key=String.valueOf(id);
+        return constantService
+                .removeById(key)? R.success():R.fail("删除失败");
+    }
+
+    @PostMapping("/save/{type}")
+    @SaCheckPermission("bus:constant:add")
+    @ApiOperation(value = "新增数据",notes = "权限【bus:constant:add】")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type",value = "常量类型",required = true,allowableValues = "doctor,alarm,mix",allowMultiple = true,dataTypeClass = ConstantEnum.class)
+    })
+    public R add(@PathVariable("type") ConstantEnum type,@RequestBody JSONObject payload) {
+        AbstractConstantService constantService = constantHashMap.get(type);
+        if(constantService==null){
+            return R.success("常量类型{"+type+"}不存在");
+        }
+        try {
+            return constantService.save( payload.toBean(constantService.getEntityClass()))?R.success(payload):R.fail("数据新增失败");
+        }catch (DuplicateKeyException e){
+            throw new CustomException("常量名称不可重复");
+        }
+    }
+
+    @PostMapping("/update/{type}")
+    @SaCheckPermission("bus:constant:edit")
+    @ApiOperation(value = "更新数据",notes = "权限【bus:constant:edit】")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type",value = "常量类型",required = true,allowableValues = "doctor,alarm,mix",allowMultiple = true,dataTypeClass = ConstantEnum.class)
+    })
+    public R update(@PathVariable("type") ConstantEnum type,@RequestBody JSONObject payload) {
+        AbstractConstantService constantService = constantHashMap.get(type);
+        if(constantService==null){
+            return R.success("常量类型{"+type+"}不存在");
+        }
+        try {
+            return constantService.updateById( payload.toBean(constantService.getEntityClass()))?R.success(payload):R.fail("数据更新失败");
+        }catch (DuplicateKeyException e){
+            throw new CustomException("常量名称不可重复");
+        }
+
+    }
+}

+ 161 - 0
nb-system/src/main/java/com/nb/bus/controller/BusDeviceController.java

@@ -0,0 +1,161 @@
+package com.nb.bus.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.mapper.Mapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.nb.bus.controller.vo.DeviceBindVo;
+import com.nb.bus.entity.BusDeviceEntity;
+import com.nb.bus.enums.DeviceAlarmEnum;
+import com.nb.bus.exception.BusinessException;
+import com.nb.bus.exception.ErrorStatus;
+import com.nb.bus.service.LocalBusDeviceService;
+import com.nb.bus.service.dto.DeviceQuery;
+import com.nb.bus.service.dto.DeviceResult;
+import com.nb.common.annotation.Log;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseCrudController;
+import com.nb.common.exception.CustomException;
+import com.nb.common.result.R;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalController.java
+ * @Description
+ * @createTime 2022年03月19日 09:28:00
+ * @accendant 龙三郎
+ */
+@Slf4j
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/device/info")
+@Api(tags = "设备管理",description = "统一权限前缀(device:info),device:info:add")
+public class BusDeviceController extends BaseCrudController<BusDeviceEntity, String> {
+    private final LocalBusDeviceService deviceService;
+
+    @Log(title = "设备换绑")
+    @PostMapping("/shift/bind")
+    @ApiOperation(value = "设备换绑",notes = "权限【device:info:shift】")
+    public R shift(@RequestBody DeviceBindVo vo){
+        if(StrUtil.isEmpty(vo.getBindTenantId())|| CollectionUtil.isEmpty(vo.getDeviceIds())){
+            throw new CustomException("选择的医院、设备不能为空");
+        }
+        deviceService.shift(vo.getDeviceIds(),vo.getBindTenantId());
+        return R.success(DeviceAlarmEnum.values());
+    }
+
+    @PostMapping("/query/page")
+    @SaCheckPermission("device:info:query")
+    @ApiOperation(value = "设备管理分页查询",notes = "权限【device:info:query】")
+    public R<IPage<DeviceResult>> pageQuery(@RequestBody DeviceQuery query){
+        return R.success(deviceService.pageQuery(query.getPage(),query));
+   }
+    /**
+     * 权限控制前缀
+     * @return
+     */
+    @Override
+    public String getPermissionPrefix() {
+        return "device:info";
+    }
+
+
+    @Override
+    public BaseService<? extends Mapper<BusDeviceEntity>, BusDeviceEntity, String> getService() {
+        return deviceService;
+    }
+
+    /**
+     * @author 龙三郎
+     * 新增设备,只有超级管理员才有此权限,如果该设备在系统中存在,则更新,如果设备在系统中不存在,在阿里云平台存在,则从阿里云获取后插入。
+     * @param payload
+     * @return
+     */
+    @Override
+    @SaCheckPermission("device:info:add")
+    @PostMapping("/add")
+    @ApiOperation(value = "新增单个数据,并返回新增后的数据. 权限【device:info:add】")
+    public R<BusDeviceEntity> add(@RequestAttribute("tenantId")@ApiParam(hidden = true) Serializable tenantId,@RequestBody BusDeviceEntity payload) {
+        log.info("添加设备");
+        // 设备id不能为空
+        if (StrUtil.isEmpty(payload.getDeviceId())){
+            throw new BusinessException(ErrorStatus.DEVICEID_IS_EMPTY);
+        }
+        // 根据deviceId从阿里云获取设备信息。
+        BusDeviceEntity device = deviceService.addDevice(payload.getDeviceId());
+        if (ObjectUtil.isNull(device)){
+            return R.fail("设备添加失败");
+        }
+        return R.success(device);
+    }
+
+    /**
+     * @author 龙三郎
+     * 从阿里云同步系统所有的设备,只有超级管理员才有此权限。将阿里云所有的设备同步到系统。
+     * @return
+     */
+    @Log(title = "从阿里云同步设备")
+    @ApiOperation(value = "从阿里云同步设备",notes = "从阿里云平台获取设备并更新,权限【device:info:poll】")
+    @SaCheckPermission("device:info:poll")
+    @PutMapping("/sync-device")
+    public R<Boolean> syncDevice() {
+        log.info("从阿里云同步设备");
+        try {
+            deviceService.syncDeviceFromIot();
+        }catch (Exception e){
+            log.error("同步设备失败,【{}】",e);
+            return R.success(false);
+        }
+        return R.success(true);
+    }
+
+
+//    /**
+//     * @author 龙三郎
+//     * 从阿里云同步系统所有的设备,只有超级管理员才有此权限。将阿里云所有的设备同步到系统。
+//     * @return
+//     */
+//    @ApiOperation(value = "从阿里云同步设备1",notes = "从阿里云平台获取设备并更新,权限【device:info:poll】")
+//    @SaCheckPermission("device:info:poll")
+//    @PutMapping("/sync-device1")
+//    public R syncDevice1() {
+//        int i = deviceService.syncAllDevice();
+//        if (i > 0){
+//            return R.success(i,"同步成功");
+//        }else {
+//            return R.fail("同步失败");
+//        }
+//    }
+
+    /**
+     * @author 龙三郎
+     * 从阿里云更新系统中存在的设备,如果设备在系统中不存在,则不更新。
+     * @return
+     */
+    @Log(title = "从阿里云更新本地设备")
+
+    @SaCheckPermission("device:info:poll")
+    @ApiOperation(value = "从阿里云更新本地设备",notes = "从阿里云平台获取设备并更新,参数为设备id列表,格式如['123456','456123'],参数可为空。权限【device:info:poll】")
+    @PutMapping("/sync-device/batch")
+    public R syncDevice(@RequestBody List<String> ids) {
+        log.info("从阿里云更新本地设备");
+        int i = deviceService.syncDevice(ids);
+        if (i > 0){
+            return R.success(i,"同步成功");
+        }else {
+            return R.fail("同步失败");
+        }
+    }
+}

+ 82 - 0
nb-system/src/main/java/com/nb/bus/controller/BusDeviceHistoryController.java

@@ -0,0 +1,82 @@
+package com.nb.bus.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.mapper.Mapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.nb.bus.entity.BusDeviceAlarmEntity;
+import com.nb.bus.entity.BusDeviceHistoryEntity;
+import com.nb.bus.service.LocalBusDeviceAlarmService;
+import com.nb.bus.service.LocalBusDeviceHistoryService;
+import com.nb.bus.service.dto.DeviceAlarmQuery;
+import com.nb.bus.service.dto.DeviceCountResult;
+import com.nb.bus.service.dto.DeviceHistoryQuery;
+import com.nb.bus.service.dto.DeviceUse;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseCrudController;
+import com.nb.common.result.R;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.AllArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalController.java
+ * @Description TODO
+ * @createTime 2022年03月19日 09:28:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/device/history")
+@Api(tags = "设备历史数据管理",description = "统一权限前缀(device:history),device:history:add")
+public class BusDeviceHistoryController extends BaseCrudController<BusDeviceHistoryEntity, String> {
+    private final LocalBusDeviceHistoryService historyService;
+    private final LocalBusDeviceAlarmService deviceAlarmService;
+
+    /**
+     * 权限控制前缀
+     * @return
+     */
+    @Override
+    public String getPermissionPrefix() {
+        return "device:history";
+    }
+
+    @PostMapping("/query/page")
+    @SaCheckPermission("bus:history:query")
+    @ApiOperation(value = "分页(输注查询)查询",notes = "权限:【bus:history:query】")
+    public R<IPage<BusDeviceHistoryEntity>> page(@RequestBody @Validated DeviceHistoryQuery query){
+        return R.success(historyService.pageQuery(query));
+    }
+
+    @PostMapping("/deviceUse")
+    @SaCheckPermission("bus:device:use")
+    @ApiOperation(value = "设备使用查询",notes = "权限:【bus:device:use】")
+    public R<List<DeviceUse>> deviceUse(@RequestAttribute(value = "tenantId",required = false)@ApiParam(hidden = true) String tenantId, @RequestBody @Validated DeviceAlarmQuery query){
+        if(!StrUtil.isNullOrUndefined(tenantId)){
+            query.setTenantId(tenantId);
+        }
+        return R.success(deviceAlarmService.selectCountAlarm(query));
+    }
+
+    @PostMapping("/deviceUseDetail")
+    @SaCheckPermission("bus:device:useDetail")
+    @ApiOperation(value = "设备使用详情",notes = "权限:【bus:device:useDetail】")
+    public R<List<DeviceCountResult>> deviceUseDetail(@RequestAttribute(value = "tenantId",required = false)@ApiParam(hidden = true) String tenantId, @RequestBody @Validated BusDeviceAlarmEntity query){
+        if(!StrUtil.isNullOrUndefined(tenantId)){
+            query.setTenantId(tenantId);
+        }
+        return R.success(deviceAlarmService.selectUseDetail(query));
+    }
+
+    @Override
+    public BaseService<? extends Mapper<BusDeviceHistoryEntity>, BusDeviceHistoryEntity, String> getService() {
+        return historyService;
+    }
+}

+ 46 - 0
nb-system/src/main/java/com/nb/bus/controller/BusDeviceManualController.java

@@ -0,0 +1,46 @@
+package com.nb.bus.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.nb.bus.service.LocalBusDeviceManualService;
+import com.nb.bus.service.dto.ManualMonitorQuery;
+import com.nb.bus.service.dto.ManualMonitorResult;
+import com.nb.bus.service.dto.MonitorTimeStatsCountResult;
+import com.nb.common.result.R;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusDrugController.java
+ * @Description TODO
+ * @createTime 2022年04月08日 10:18:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/device/manual")
+@Api(tags = "其他监控(即手动添加设备管理)")
+public class BusDeviceManualController {
+    private final LocalBusDeviceManualService deviceManualService;
+
+    @PostMapping("/no_page")
+    @SaCheckPermission("device:manual:query")
+    @ApiOperation(value = "查询其他监控输注列表,不分页",notes = "查询其他监控输注列表,权限【device:manual:query】")
+    public R<List<ManualMonitorResult>> list(@RequestBody ManualMonitorQuery query){
+        return R.success(deviceManualService.selectMonitor(query));
+    }
+
+    @PostMapping("/stats/time")
+    @SaCheckPermission("device:manual:query")
+    @ApiOperation(value = "按照时间统计病人监控数量",notes = "权限【device:manual:query】")
+    public R<MonitorTimeStatsCountResult> statsTime(){
+        return R.success(deviceManualService.timeStats(null));
+    }
+}

+ 49 - 0
nb-system/src/main/java/com/nb/bus/controller/BusDeviceRunningController.java

@@ -0,0 +1,49 @@
+package com.nb.bus.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.nb.bus.controller.vo.NoPumpConfigVo;
+import com.nb.bus.enums.DeviceAlarmEnum;
+import com.nb.bus.enums.DeviceStatusEnum;
+import com.nb.common.result.R;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalController.java
+ * @Description
+ * @createTime 2022年03月19日 09:28:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/device/running")
+@Api(tags = "设备运行状态管理")
+public class BusDeviceRunningController {
+
+    @GetMapping("/alarm/dict")
+    @ApiOperation(value = "设备报警状态枚举,无权限配置, alarm字段表示该状态是否为报警状态")
+    public R alarm() {
+        return R.success(Arrays.stream(DeviceAlarmEnum.values()).filter(alarmEnum->!DeviceAlarmEnum.None.equals(alarmEnum)).collect(Collectors.toList()));
+    }
+
+    @GetMapping("/status/dict")
+    @ApiOperation(value = "设备运行状态枚举,无权限配置")
+    public R status() {
+        return R.success(DeviceStatusEnum.values());
+    }
+
+    @PostMapping("/nopump")
+    @SaCheckPermission("device:running:add")
+    @ApiOperation(value = "新增无泵数据,权限为device:running:add")
+    public R saveNoPump(@RequestBody NoPumpConfigVo noPump) {
+        return R.success();
+    }
+
+
+}

+ 55 - 0
nb-system/src/main/java/com/nb/bus/controller/BusDocController.java

@@ -0,0 +1,55 @@
+package com.nb.bus.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.Mapper;
+import com.nb.bus.entity.BusDocEntity;
+import com.nb.bus.service.LocalBusDocService;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseCrudController;
+import com.nb.common.result.R;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalController.java
+ * @Description TODO
+ * @createTime 2022年03月19日 09:28:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/doc")
+@Api(tags = "医院宣教文档",value = "该接口用于处理医院的帮助文档,统一权限前缀(bus:doc),bus:doc:add")
+public class BusDocController extends BaseCrudController<BusDocEntity, String> {
+    private final LocalBusDocService docService;
+
+
+    /**
+     * 权限控制前缀
+     * @return
+     */
+    @Override
+    public String getPermissionPrefix() {
+        return "bus:doc";
+    }
+
+
+    @GetMapping("/get/{type}")
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "type",value = "文档分类",required = true,example = "文档分类")
+    })
+    @ApiOperation(value = "获取当前医院的宣教文档",notes = "当用户未登录、用户为系统用户、用户租户id为空时,返回值为空,权限为空")
+    public R getCurrentHospital(@PathVariable(value = "type") String type){
+        return  R.success(docService.getOne(new QueryWrapper<BusDocEntity>().lambda().eq(BusDocEntity::getType,type)));
+    }
+
+    @Override
+    public BaseService<? extends Mapper<BusDocEntity>, BusDocEntity, String> getService() {
+        return docService;
+    }
+}

+ 41 - 0
nb-system/src/main/java/com/nb/bus/controller/BusDrugController.java

@@ -0,0 +1,41 @@
+package com.nb.bus.controller;
+
+import com.baomidou.mybatisplus.core.mapper.Mapper;
+import com.nb.bus.entity.BusDrugEntity;
+import com.nb.bus.service.LocalBusDrugService;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseCrudController;
+import io.swagger.annotations.Api;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusDrugController.java
+ * @Description TODO
+ * @createTime 2022年04月08日 10:18:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/drug")
+@Api(tags = "药品管理",description = "统一权限前缀(bus:drug),例如新增bus:drug:add")
+public class BusDrugController extends BaseCrudController<BusDrugEntity,String> {
+    private final LocalBusDrugService drugService;
+
+    /**
+     * 权限控制前缀
+     * @return
+     */
+    @Override
+    public String getPermissionPrefix() {
+        return "bus:drug";
+    }
+
+    @Override
+    public BaseService<? extends Mapper<BusDrugEntity>, BusDrugEntity, String> getService() {
+        return drugService;
+    }
+
+}

+ 57 - 0
nb-system/src/main/java/com/nb/bus/controller/BusEvaluationController.java

@@ -0,0 +1,57 @@
+package com.nb.bus.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.core.mapper.Mapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.nb.bus.entity.BusEvaluationEntity;
+import com.nb.bus.service.LocalBusEvaluationService;
+import com.nb.bus.service.dto.EvalQuery;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseCrudController;
+import com.nb.common.result.R;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalController.java
+ * @Description
+ * @createTime 2022年03月19日 09:28:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/eval")
+@Api(tags = "评价管理",description = "统一权限前缀(bus:eval),bus:eval:add")
+public class BusEvaluationController extends BaseCrudController<BusEvaluationEntity, String> {
+    private final LocalBusEvaluationService evaluationService;
+
+
+
+    @PostMapping("/query/page")
+    @SaCheckPermission("bus:eval:query")
+    @ApiOperation(value = "评价(输注查询)查询",notes = "权限:【bus:eval:query】")
+    public R<IPage<BusEvaluationEntity>> page(@RequestBody @Validated EvalQuery query){
+        return R.success(evaluationService.pageQuery(query));
+    }
+
+    /**
+     * 权限控制前缀
+     * @return
+     */
+    @Override
+    public String getPermissionPrefix() {
+        return "bus:eval";
+    }
+
+    @Override
+    public BaseService<? extends Mapper<BusEvaluationEntity>, BusEvaluationEntity, String> getService() {
+        return evaluationService;
+    }
+}

+ 42 - 0
nb-system/src/main/java/com/nb/bus/controller/BusFormulaController.java

@@ -0,0 +1,42 @@
+package com.nb.bus.controller;
+
+import com.baomidou.mybatisplus.core.mapper.Mapper;
+import com.nb.bus.entity.BusFormulaEntity;
+import com.nb.bus.service.LocalBusFormulaService;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseCrudController;
+import io.swagger.annotations.Api;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author zsl
+ * @version 1.0.0
+ * @ClassName BusFormulaController.java
+ * @Description
+ * @createTime 2022/4/20 8:25
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/formula")
+@Api(tags = "药品配方管理",description = "统一权限前缀(bus:formula),bus:formula:add")
+
+public class BusFormulaController  extends BaseCrudController<BusFormulaEntity, String> {
+    private final LocalBusFormulaService scoreService;
+
+    /**
+     * 权限控制前缀
+     * @return
+     */
+    @Override
+    public String getPermissionPrefix() {
+        return "bus:formula";
+    }
+
+
+    @Override
+    public BaseService<? extends Mapper<BusFormulaEntity>, BusFormulaEntity, String> getService() {
+        return scoreService;
+    }
+}

+ 57 - 0
nb-system/src/main/java/com/nb/bus/controller/BusHospitalConfigController.java

@@ -0,0 +1,57 @@
+package com.nb.bus.controller;
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.baomidou.mybatisplus.core.mapper.Mapper;
+import com.nb.bus.entity.BusHospitalConfigEntity;
+import com.nb.bus.enums.ConfigEnum;
+import com.nb.bus.service.LocalBusHospitalConfigService;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseCrudController;
+import com.nb.common.exception.CustomException;
+import com.nb.common.result.R;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalController.java
+ * @Description TODO
+ * @createTime 2022年03月19日 09:28:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/hospital/config")
+@Api(tags = "医院功能配置",value = "统一权限前缀(hospital:config),hospital:config:add")
+public class BusHospitalConfigController extends BaseCrudController<BusHospitalConfigEntity, String> {
+    private final LocalBusHospitalConfigService hospitalConfigService;
+
+
+    /**
+     * 权限控制前缀
+     * @return
+     */
+    @Override
+    public String getPermissionPrefix() {
+        return "hospital:config";
+    }
+
+    @PostMapping("/reset/{type}")
+    @SaCheckPermission("hospital:config:edit")
+    @ApiOperation(value = "将某一功能项改为默认配置",notes = "权限【hospital:config:edit】")
+    public R<Boolean> reset(@RequestAttribute("tenantId")String tenantId,@PathVariable("type") int type){
+        ConfigEnum configEnum = ConfigEnum.valueOf(type);
+        if(configEnum==null){
+            throw new CustomException("无此配置项");
+        }
+        hospitalConfigService.setDefaultConfig(tenantId,configEnum);
+         return R.success(true);
+    }
+
+    @Override
+    public BaseService<? extends Mapper<BusHospitalConfigEntity>, BusHospitalConfigEntity, String> getService() {
+        return hospitalConfigService;
+    }
+}

+ 147 - 0
nb-system/src/main/java/com/nb/bus/controller/BusHospitalController.java

@@ -0,0 +1,147 @@
+package com.nb.bus.controller;
+
+
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.annotation.SaMode;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+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.nb.bus.bean.Script;
+import com.nb.bus.controller.vo.ExecScript;
+import com.nb.bus.entity.BusHospitalEntity;
+import com.nb.bus.entity.HisUpdateEntity;
+import com.nb.bus.hospital.HospitalManagerRegister;
+import com.nb.bus.hospital.his.strategy.HisStrategyEnum;
+import com.nb.bus.hospital.script.ScriptManager;
+import com.nb.bus.service.LocalBusHospitalService;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseCrudController;
+import com.nb.common.exception.CustomException;
+import com.nb.common.result.R;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalController.java
+ * @Description TODO
+ * @createTime 2022年03月19日 09:28:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/hospital")
+@Api(tags = "医院管理",description = "统一权限前缀(bus:hospital),例如新增bus:hospital:add")
+public class BusHospitalController extends BaseCrudController<BusHospitalEntity, String> {
+    private final LocalBusHospitalService hospitalService;
+    private final HospitalManagerRegister hospitalManagerRegister;
+    private final ScriptManager scriptManager;
+
+    /**
+     * 权限控制前缀
+     * @return
+     */
+    @Override
+    public String getPermissionPrefix() {
+        return "bus:hospital";
+    }
+
+    @Override
+    public BaseService<? extends Mapper<BusHospitalEntity>, BusHospitalEntity, String> getService() {
+        return hospitalService;
+    }
+
+    @PostMapping("/{id}/script")
+    @SaCheckPermission("bus:hospital:script:save")
+    @ApiOperation(value = "保存医院信息解析草稿脚本",notes = "保存医院信息解析脚本,权限【bus:hospital:script:save】")
+    public R script(@PathVariable("id") String id,@RequestBody Script script){
+        BusHospitalEntity busHospitalEntity = new BusHospitalEntity();
+        busHospitalEntity.setId(id);
+        busHospitalEntity.setDraftScript(script);
+        if (this.getService()
+                .updateById(busHospitalEntity)) {
+            scriptManager.resetScript(id,script.getId(),script.getContent());
+        }
+        return R.success();
+    }
+
+    @PostMapping("/debug")
+    @SaCheckPermission("bus:hospital:script:query")
+    @ApiOperation(value = "执行解析脚本",notes = "医院必选,根据入参执行解析脚本,权限【bus:hospital:script:query】")
+    public R debug(@RequestBody ExecScript execScript){
+        return R.success( scriptManager.debug(execScript.getContent(),execScript.getInput()));
+    }
+
+    @PostMapping("/draft/script")
+    @SaCheckPermission("bus:hospital:script:edit")
+    @ApiOperation(value = "保存脚本草稿脚本",notes = "医院必选,保存脚本草稿脚本,权限【bus:hospital:script:edit】")
+    public R draftScript(@RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId, @RequestBody Script script){
+        scriptManager.check(script.getContent(),script.getType());
+        hospitalService.update(new UpdateWrapper<BusHospitalEntity>().lambda().eq(BusHospitalEntity::getId,tenantId)
+                .set(BusHospitalEntity::getDraftScript, JSONUtil.toJsonStr(script)));
+        return R.success(true);
+    }
+
+    @PostMapping("/publish/script")
+    @SaCheckPermission("bus:hospital:script:pub")
+    @ApiOperation(value = "发布脚本",notes = "医院必选,发布脚本,发布后即用该脚本解析his数据,权限【bus:hospital:script:pub】")
+    public R publishScript(@RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId){
+        BusHospitalEntity hospital = hospitalService.getById(tenantId);
+        if (ObjectUtil.isNull(hospital.getDraftScript())||StrUtil.isEmpty(hospital.getDraftScript().getContent())) {
+            throw new CustomException("草稿脚本内容为空,发布失败");
+        }
+        hospitalService.update(new UpdateWrapper<BusHospitalEntity>().lambda().eq(BusHospitalEntity::getId,tenantId)
+                .set(BusHospitalEntity::getScript,JSONUtil.toJsonStr(hospital.getDraftScript())));
+        hospitalManagerRegister.refresh(tenantId,false,false,true);
+        return R.success(true);
+    }
+
+    @PostMapping("/strategy/{type}")
+    @SaCheckPermission("bus:hospital:strategy")
+    @ApiOperation(value = "His对接策略编辑",notes = "His对接策略编辑,权限【bus:hospital:strategy】")
+    public R editStrategy(@PathVariable("type")@ApiParam("接收his数据的策略, 0、无his 1(默认)、获取病人全部信息 2、获取病人部分信息 3、获取病人最新信息") Integer type, @RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId, @RequestBody HisUpdateEntity updateConfig){
+        HisStrategyEnum strategy = HisStrategyEnum.valueOf(type);
+        if(strategy==null){
+            throw new CustomException("所选策略不存在");
+        }
+        hospitalService.update(new UpdateWrapper<BusHospitalEntity>()
+                .lambda()
+                .eq(BusHospitalEntity::getId,tenantId)
+                .set(null!=updateConfig,BusHospitalEntity::getUpdateConfig,JSONUtil.toJsonStr(updateConfig))
+                .set(BusHospitalEntity::getStrategy,strategy.getValue()));
+        if(updateConfig!=null){
+            hospitalManagerRegister.get(tenantId)
+                    .refreshUpdateConfig(updateConfig);
+        }
+        return R.success(true);
+    }
+
+
+    @PostMapping("/validate")
+    @SaCheckPermission(mode = SaMode.OR,value = {"bus:hospital:edit","bus:hospital:add"})
+    @ApiOperation(value = "校验医院名称是否重复",notes = "权限【bus:hospital:edit 或 bus:hospital:edit】")
+    public R<Boolean> validate(@RequestBody String name){
+        return R.success(this.hospitalService.isExistName(name));
+    }
+
+
+    @PostMapping("/look/script")
+    @SaCheckPermission("bus:hospital:script:query")
+    @ApiOperation(value = "查看医院脚本信息",notes = "His对接策略编辑,权限【bus:hospital:script:query】")
+    public R<BusHospitalEntity> lookScript(@RequestAttribute("tenantId")@ApiParam(hidden = true) String id){
+        return R.success(this.hospitalService.getOne(new QueryWrapper<BusHospitalEntity>().lambda()
+                .select(BusHospitalEntity::getId,BusHospitalEntity::getScript,BusHospitalEntity::getStrategy,
+                        BusHospitalEntity::getDraftScript,
+                        BusHospitalEntity::getUpdateConfig)
+                .eq(BusHospitalEntity::getId,id)));
+    }
+
+
+
+}

+ 41 - 0
nb-system/src/main/java/com/nb/bus/controller/BusHospitalLogController.java

@@ -0,0 +1,41 @@
+package com.nb.bus.controller;
+
+import com.baomidou.mybatisplus.core.mapper.Mapper;
+import com.nb.bus.entity.BusHospitalLogEntity;
+import com.nb.bus.service.LocalBusHospitalLogService;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseCrudController;
+import io.swagger.annotations.Api;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalController.java
+ * @Description TODO
+ * @createTime 2022年03月19日 09:28:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/log/hospital")
+@Api(tags = "医院传输日志管理",description = "统一权限前缀(log:hospital),例如新增log:hospital:add")
+public class BusHospitalLogController extends BaseCrudController<BusHospitalLogEntity, String> {
+    private final LocalBusHospitalLogService logService;
+
+
+    /**
+     * 权限控制前缀
+     * @return
+     */
+    @Override
+    public String getPermissionPrefix() {
+        return "log:hospital";
+    }
+
+    @Override
+    public BaseService<? extends Mapper<BusHospitalLogEntity>, BusHospitalLogEntity, String> getService() {
+        return logService;
+    }
+}

+ 59 - 0
nb-system/src/main/java/com/nb/bus/controller/BusInfusionHistoryController.java

@@ -0,0 +1,59 @@
+package com.nb.bus.controller;
+
+import cn.dev33.satoken.SaManager;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.stp.StpLogic;
+import com.baomidou.mybatisplus.core.mapper.Mapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.nb.bus.entity.BusInfusionHistoryEntity;
+import com.nb.bus.service.LocalBusInfusionHistoryService;
+import com.nb.bus.service.dto.CombineQuery;
+import com.nb.bus.service.dto.CombineResult;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseQueryController;
+import com.nb.common.result.R;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusInfusionHistoryController.java
+ * @Description TODO
+ * @createTime 2022年05月27日 15:44:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/infusion")
+@Api(tags = "输注历史管理",description = "输注历史管理,查询操作权限【bus:infusion:query】")
+public class BusInfusionHistoryController implements BaseQueryController<BusInfusionHistoryEntity, String> {
+    private final LocalBusInfusionHistoryService infusionHistoryService;
+
+    @Override
+    public BaseService<? extends Mapper<BusInfusionHistoryEntity>, BusInfusionHistoryEntity, String> getService() {
+        return infusionHistoryService;
+    }
+
+    @PostMapping("/combine/page")
+    @SaCheckPermission("bus:infusion:query")
+    @ApiOperation(value = "输注历史查询(此查询中包括了所属的临床信息)",notes = "权限【bus:infusion:query】")
+    public R<IPage<CombineResult>> compQuery(@RequestBody CombineQuery query){
+        return R.success(infusionHistoryService.queryPage(query,query.getPage()));
+    }
+
+
+    @Override
+    public String getPermissionPrefix() {
+        return "bus:infusion";
+    }
+
+    @Override
+    public StpLogic getStpLogin() {
+        return SaManager.getStpLogic("");
+    }
+}

+ 372 - 0
nb-system/src/main/java/com/nb/bus/controller/BusPatientController.java

@@ -0,0 +1,372 @@
+package com.nb.bus.controller;
+
+import cn.dev33.satoken.SaManager;
+import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.dev33.satoken.stp.StpLogic;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.text.CharSequenceUtil;
+import cn.hutool.json.JSONUtil;
+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.nb.bus.controller.vo.GetPatientInfoVo;
+import com.nb.bus.controller.vo.MonitorDetailVo;
+import com.nb.bus.controller.vo.MonitorFinishedVo;
+import com.nb.bus.entity.*;
+import com.nb.bus.enums.DeviceStatusEnum;
+import com.nb.bus.enums.PatientAlarmEnum;
+import com.nb.bus.registry.patient.PatientOperator;
+import com.nb.bus.registry.patient.PatientRegistry;
+import com.nb.bus.service.*;
+import com.nb.bus.service.dto.*;
+import com.nb.bus.utils.WsPublishUtils;
+import com.nb.common.annotation.Log;
+import com.nb.common.crud.BaseService;
+import com.nb.common.crud.controller.BaseQueryController;
+import com.nb.common.exception.CustomException;
+import com.nb.common.result.R;
+import io.swagger.annotations.*;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.context.request.async.DeferredResult;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusHospitalController.java
+ * @Description TODO
+ * @createTime 2022年03月19日 09:28:00
+ */
+@RestController
+@AllArgsConstructor
+@RequestMapping("/bus/patient")
+@Slf4j
+@Api(tags = "医院病人管理",description = "关于病人的相关操作,查询操作权限【bus:patient:query】")
+public class BusPatientController  implements BaseQueryController<BusPatientEntity,String> {
+    private final LocalBusPatientService patientService;
+
+    private final LocalBusClinicService clinicService;
+
+    private final PatientRegistry patientRegistry;
+
+    private final LocalBusInfusionHistoryService infusionService;
+
+    private final LocalBusDeviceManualService manualService;
+
+    private final WsPublishUtils wsPublishUtils;
+    @PostMapping("/no_page")
+    @SaCheckPermission("device:patient:query")
+    @ApiOperation(value = "输注监控列表",notes = "病人监控管理列表,权限【device:patient:query】")
+    public R<List<PatientMonitorResult>> selectPage(@RequestBody PatientMonitorQuery query){
+        log.info("输注监控列表,【{}】",JSONUtil.toJsonStr(query));
+        return R.success(patientService.selectAll(query));
+    }
+
+
+    @Log(title = "查询该次临床过程中所用的所有输注信息")
+    @PostMapping("/look/{clinicId}/infusion")
+    @SaCheckPermission("bus:patient:query")
+    @ApiOperation(value = "查询该次临床过程中所用的所有输注信息",notes = "查询该次临床过程中所用的所有输注信息,权限【bus:patient:query】")
+    public R lookUpAllInfusion(@PathVariable("clinicId") String clinicId){
+        List<BusInfusionHistoryEntity> infusionHistories = infusionService.list(new QueryWrapper<BusInfusionHistoryEntity>().lambda().eq(BusInfusionHistoryEntity::getClinicId, clinicId));
+        return R.success(infusionHistories.stream()
+                .map(InfusionHistorySmallResult::of)
+                .sorted(Comparator.comparing(InfusionHistorySmallResult::getStartTime))
+                .collect(Collectors.toList()));
+    }
+
+    @GetMapping("/repeat/device")
+    @ApiOperation(value = "设备重复提示列表",notes = "当出现病患同一时间绑定了多个泵时,调用该接口查询出所有设备重复报警数据,权限【无】")
+    public R<List<PatientDeviceRepeatResult>> repeatDevice(){
+        log.info("设备重复提示列表");
+        return R.success(patientService.repeatDevice());
+    }
+
+    @GetMapping("/none/device")
+    @ApiOperation(value = "临床无泵列表",notes = "当出现病患同一时间没有泵时,调用该接口查询出所有没有泵信息的病患临床数据,权限【无】")
+    public R<List<PatientDeviceNoneResult>> noneDevice(){
+        log.info("设备重复提示列表");
+        return R.success(patientService.noneDevice());
+    }
+
+    @PostMapping("/judge/repeat")
+    @ApiOperation(value = "判断所给病号中是否出现了绑定设备重复",notes = "当结束管理时,调用改接口判断所结束病号是否出现了设备重复异常,权限【无】")
+    @ApiResponses({
+            @ApiResponse(code = 200,message = "0 病人中未出现重复,非0 病人中出现重复"),
+    })
+    public R<Long> judgeRepeat(@RequestBody List<String> patientCode){
+        log.info("判断所给病号中是否出现了绑定设备重复,【{}】",patientCode);
+        return R.success(patientService.count(new QueryWrapper<BusPatientEntity>().lambda().in(BusPatientEntity::getCode,patientCode).eq(BusPatientEntity::getAlarm,PatientAlarmEnum.DEVICE_REPEAT)));
+    }
+
+    @PostMapping("/judge/finished")
+    @ApiOperation(value = "判断给定病号中是否可以结束临床管理",notes = "病患当前绑定主设备必须要在关机、不在服务器、待机中才能结束管理,权限【无】")
+    @ApiResponses({
+            @ApiResponse(code = 500,message = "没有选择病号"),
+            @ApiResponse(code =200 ,message = "若存在不可以结束的临床信息,返回1",response = BusDeviceRunningEntity.class)
+    })
+    public R<Boolean> judgeFinished(@RequestBody List<String> patientCodes){
+        log.info("判断给定病号中是否可以结束临床管理,【{}】",patientCodes);
+        if(CollUtil.isEmpty(patientCodes)){
+            throw new CustomException("请选择一个病患");
+        }
+        List<BusPatientEntity> patients = patientService.list(new QueryWrapper<BusPatientEntity>()
+                .lambda().in(BusPatientEntity::getCode, patientCodes));
+        if(CollUtil.isEmpty(patients)){
+            throw new CustomException("病号不存在,请刷新后重试");
+        }
+        return R.success(infusionService.count(new QueryWrapper<BusInfusionHistoryEntity>()
+                .lambda()
+                .nested(i->i.ne(BusInfusionHistoryEntity::getRunState,DeviceStatusEnum.Shutdown)
+                        .ne(BusInfusionHistoryEntity::getRunState,DeviceStatusEnum.NoSignal))
+                .in(BusInfusionHistoryEntity::getPatientId,patients.stream().map(BusPatientEntity::getId).collect(Collectors.toList()))
+                .eq(BusInfusionHistoryEntity::getFinished,false))!=0);
+    }
+
+    @PostMapping("/do/{monitorType}/finished")
+    @SaCheckPermission("bus:patient:finished")
+    @ApiResponse(code = 4001,message = "病号当前绑定了多个设备,不可结束管理")
+    @ApiOperation(value = "结束管理",notes = "病患当前绑定主设备必须要在关机、不在服务器、待机中才能结束管理,权限【bus:patient:finished】")
+    public R<Boolean> finished(@PathVariable("monitorType")@ApiParam(value = "是否为无泵管理 false、无泵 true、有泵",defaultValue = "false" ) boolean haveDevice,
+                               @RequestBody MonitorFinishedVo monitorFinishedVo,
+                               @RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId){
+        log.info("结束管理,【{}】",JSONUtil.toJsonStr(monitorFinishedVo));
+        if(haveDevice){
+            R<Boolean> result = monitorFinished(monitorFinishedVo, tenantId);
+            CompletableFuture.runAsync(()->wsPublishUtils.publishMonitorTotalCount(tenantId))
+                    .thenRunAsync(()->wsPublishUtils.publishDeviceNone(tenantId))
+                    .thenRunAsync(()->wsPublishUtils.publishMonitorStateCount(tenantId));
+            return result;
+        }else {
+            return manualFinished(monitorFinishedVo);
+        }
+    }
+
+    /**
+     * 描述: 输注监控结束管理(即使用驼人网络泵产品)
+     * @author lifang
+     * @date 2022/5/10 11:48
+     * @param monitorFinishedVo
+     * @param tenantId
+     * @return R
+     */
+    private R<Boolean> monitorFinished(MonitorFinishedVo monitorFinishedVo, String tenantId){
+        List<String> patientCodes = monitorFinishedVo.getPatientCodes();
+        if(CollUtil.isEmpty(patientCodes)){
+            List<String> clinicIds = monitorFinishedVo.getClinicIds();
+            if (CollUtil.isEmpty(clinicIds)) {
+                throw new CustomException("未选择临床信息");
+            }
+        }
+        List<BusPatientEntity> patients = patientService.list(new QueryWrapper<BusPatientEntity>().lambda().in(BusPatientEntity::getCode, patientCodes));
+        List<ManualUndoConfig> undoConfigs=new ArrayList<>();
+        for (BusPatientEntity patient : patients) {
+            undoConfigs.add(
+                    ManualUndoConfig.of(
+                            Collections.singletonList(patient.getInfusionId()),
+                            patient.getCode(),
+                            patient.getClinicId(),
+                            tenantId,
+                            true,
+                            monitorFinishedVo.getUndo())
+            );
+        }
+        //病患绑定的有设备,对设备进行撤泵操作,且结束临床
+        infusionService.batchUndo(undoConfigs,true);
+        return R.success(true);
+    }
+
+    /**
+     * 描述: 其他监控手动结束管理(即未采用驼人网络泵产品)
+     * @author lifang
+     * @date 2022/5/10 11:49
+     * @param monitorFinishedVo
+     * @return R
+     */
+    private R<Boolean> manualFinished(MonitorFinishedVo monitorFinishedVo){
+        List<String> clinicIds = monitorFinishedVo.getClinicIds();
+        if (CollUtil.isEmpty(clinicIds)) {
+            throw new CustomException("未选择临床信息");
+        }
+        manualService.finishedMonitor(monitorFinishedVo);
+        return R.success(true);
+    }
+
+
+    @PostMapping("/monitor/reset/{clinicId}")
+    @SaCheckPermission("device:patient:edit")
+    @ApiOperation(value = "病人当前监控时间重启",notes = "当结束临床后有新的输注信息产生,那么病人监控会重新显示,此时监控时间不会重新计算,需先调用该接口,权限标识为【device:patient:edit】")
+    public R<Boolean> reset(@ApiParam("临床id")@PathVariable("clinicId")String clinicId){
+        log.info("病人当前监控时间重启,【{}】",clinicId);
+        R<Boolean> result = R.success(clinicService.update(new UpdateWrapper<BusClinicEntity>().lambda().eq(BusClinicEntity::getId, clinicId)
+                .set(BusClinicEntity::getEndTime, null)));
+        BusClinicEntity clinic = clinicService.getById(clinicId);
+        wsPublishUtils.publishPatientMonitor(clinic.getPatientId(),clinic.getTenantId());
+        return result;
+    }
+
+    @PostMapping("/{alarm}/_count")
+    @SaCheckPermission("device:patient:query")
+    @ApiOperation(value = "病人报警数量统计",notes = "病人报警数量统计 0、未报警 1、泵重复 2、无泵,权限标识为【device:patient:query】")
+    public R<Long> alarmCount(@PathVariable("alarm") int alarm){
+        PatientAlarmEnum alarmEnum = PatientAlarmEnum.of(alarm);
+        if(alarmEnum==null){
+            return R.success(0L);
+        }
+        log.info("病人报警数量统计,【{}】",alarmEnum);
+        return R.success(patientService.patientAlarmCount(alarmEnum));
+    }
+
+    @PostMapping("/shift")
+    @SaCheckPermission("device:patient:shift")
+    @ApiOperation(value = "主泵切换的操作,只切换,不结束",notes = "当出现泵重复状态时,若用户想要进行主泵的切换,调用该接口进行操作,主泵切换完成后,会将副泵自动撤泵,权限标识为【device:patient:shift】")
+    public R shift(@RequestBody@Validated DeviceShiftConfig shiftConfig){
+        log.info("主泵切换的操作,【{}】",JSONUtil.toJsonStr(shiftConfig));
+        patientService.shift(shiftConfig);
+        wsPublishUtils.publishPatientMonitor(shiftConfig.getPatientId(),shiftConfig.getTenantId());
+        return R.success();
+    }
+
+    @PostMapping("/undo")
+    @SaCheckPermission("device:patient:undo")
+    @ApiOperation(value = "批量撤泵,只撤泵,不切换",notes = "当出现泵重复状态时,若用户想要取消对其他副泵的监控,则调用此接口进行撤泵操作,权限标识为【device:patient:undo】")
+    public R shift(@RequestBody@Validated ManualUndoConfig undoConfig, @RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId){
+        log.info("批量撤泵,【{}】",JSONUtil.toJsonStr(undoConfig));
+        undoConfig.setTenantId(tenantId);
+        BusPatientEntity patient = patientService.findByFormatCode( undoConfig.getPatientCode());
+        if(patient==null){
+            throw new CustomException(String.format("病号【%s】不存在,请刷新后重试",undoConfig.getPatientCode()));
+        }
+        //泵切换完成后,对病号报警解除
+        infusionService.undo(undoConfig,false);
+        PatientOperator operator = patientRegistry.getOperator(undoConfig.getTenantId(), undoConfig.getPatientCode());
+        //判断当前病号下是否还存在副泵
+        long count = infusionService.count(new QueryWrapper<BusInfusionHistoryEntity>().lambda()
+                .eq(BusInfusionHistoryEntity::getClinicId, operator.getClinicId())
+                .eq(BusInfusionHistoryEntity::getFinished,false)
+                .eq(BusInfusionHistoryEntity::getIsUndo,false));
+        //处理缓存信息
+        if(count==1){
+            patientService.update(new UpdateWrapper<BusPatientEntity>().lambda()
+                    .eq(BusPatientEntity::getId,patient.getId())
+                    .set(BusPatientEntity::getAlarm, PatientAlarmEnum.NONE));
+            wsPublishUtils.publishDeviceRepeat(undoConfig.getTenantId());
+        }
+        wsPublishUtils.publishPatientMonitor(patient.getId(),undoConfig.getTenantId());
+        return R.success();
+    }
+
+
+    @PostMapping("/monitor")
+    @SaCheckPermission("device:patient:query")
+    @ApiOperation(value = "查看病人当前监控详情",notes = "查看病人当前监控详情,权限标识为【device:patient:query】")
+    public R<PatientMonitorDetailResult> monitor(@RequestBody@Validated MonitorDetailVo vo){
+        log.info("查看病人监控详情,【{}】", JSONUtil.toJsonStr(vo));
+        PatientMonitorDetailResult result = new PatientMonitorDetailResult();
+        BusClinicEntity clinic =null;
+        Boolean monitorType = vo.getMonitorType();
+        if(Boolean.TRUE.equals(monitorType)){
+            String infusionId = vo.getInfusionId();
+            if(CharSequenceUtil.isAllBlank(infusionId)){
+                String patientCode = vo.getPatientCode();
+                BusPatientEntity patient = patientService.findByFormatCode(patientCode);
+                if(patient==null){
+                    throw new CustomException("该病号信息不存在,请刷新后重试");
+                }
+                clinic = clinicService.getById(patient.getClinicId());
+                infusionId=patient.getInfusionId();
+            }else {
+                clinic= clinicService.getById(vo.getClinicId());
+            }
+            BusInfusionHistoryEntity infusion =infusionService.getById(infusionId);
+            result.setInfusion(infusion);
+        }else {
+            String clinicId = vo.getClinicId();
+            clinic = clinicService.getById(clinicId);
+            result.setClinic(clinic);
+            if(clinic!=null){
+                //无泵查看
+                result.setDeviceManual( manualService.getOne(new QueryWrapper<BusDeviceManualEntity>().lambda().eq(BusDeviceManualEntity::getClinicId, clinic.getId())));
+            }
+        }
+        if(clinic==null){
+            throw new CustomException("该临床信息不存在,请刷新后重试");
+        }
+        result.setClinic(clinic);
+        return R.success(result);
+    }
+
+    @PostMapping("/stats/status")
+    @SaCheckPermission("device:patient:query")
+    @ApiOperation(value = "按照状态统计病人监控状态数量",notes = "权限【device:patient:query】")
+    public R<MonitorStatusStatsCountResult> statsStatus(){
+        return R.success(patientService.statusStats(null));
+    }
+
+
+    @PostMapping("/stats/time")
+    @SaCheckPermission("device:patient:query")
+    @ApiOperation(value = "按照时间统计病人监控数量",notes = "权限【device:patient:query】")
+    public R<MonitorTimeStatsCountResult> statsTime(){
+        return R.success(patientService.timeStats(null));
+    }
+
+
+
+    @PostMapping("/pull/async")
+    @SaCheckPermission("device:patient:pull")
+    @ApiOperation(value = "异步更新患者信息,超时时间默认为10s,超时后数据返回继续处理,输注监控",notes = "权限标识为【bus:patient:pull】")
+    public DeferredResult<R<BusClinicEntity>> syn(@RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId, @Validated@RequestBody GetPatientInfoVo vo){
+        return patientService.getPatientInfoFromHis(tenantId,vo.getPatientCode(),vo.getTimeout(),false);
+    }
+
+
+    @SaCheckPermission("device:patient:query")
+    @ApiOperation(value = "泵重复绑定数量",notes = "权限标识为【bus:patient:query】")
+    @PostMapping("/_count/repeat")
+    public R<Integer> repeatCount(){
+        return R.success(CollUtil.size(patientService.repeatDevice()));
+    }
+
+    @SaCheckPermission("device:patient:query")
+    @ApiOperation(value = "无泵数量",notes = "权限标识为【bus:patient:query】")
+    @PostMapping("/_count/none")
+    public R<Integer> noneCount(){
+        return R.success(CollUtil.size(patientService.repeatDevice()));
+    }
+    /**
+     * 描述:
+     * @author lifang
+     * @date 2022/5/15 21:56
+     * @param tenantId
+     * @param vo
+     * @return R
+     */
+    @PostMapping("/pull/sync")
+    @SaCheckPermission("device:patient:pull")
+    @ApiOperation(value = "同步更新患者信息,超时时间默认为10s,超时后数据返回则不进行处理,无泵更新",notes = "权限标识为【bus:patient:pull】")
+    public DeferredResult<R<BusClinicEntity>>  async(@RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId,@Validated @RequestBody GetPatientInfoVo vo){
+        return patientService.getPatientInfoFromHis(tenantId,vo.getPatientCode(),vo.getTimeout(),true);
+    }
+
+
+    @Override
+    public BaseService<? extends Mapper<BusPatientEntity>, BusPatientEntity, String> getService() {
+        return patientService;
+    }
+
+    @Override
+    public String getPermissionPrefix() {
+        return "bus:patient:";
+    }
+
+    @Override
+    public StpLogic getStpLogin() {
+        return SaManager.getStpLogic("");
+    }
+}

+ 72 - 0
nb-system/src/main/java/com/nb/bus/controller/BusStatsAnalyseController.java

@@ -0,0 +1,72 @@
+package com.nb.bus.controller;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import com.nb.bus.enums.StatsAnalyseEnum;
+import com.nb.bus.service.LocalBusInfusionHistoryService;
+import com.nb.bus.service.dto.CombineQuery;
+import com.nb.bus.service.dto.CombineResult;
+import com.nb.bus.stats.CommonStats;
+import com.nb.bus.stats.entity.StatsAnalyseResult;
+import com.nb.common.exception.CustomException;
+import com.nb.common.exception.TenantException;
+import com.nb.common.result.R;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusAnalyseController.java
+ * @Description TODO
+ * @createTime 2022年04月08日 10:24:00
+ */
+@RestController
+@RequestMapping("/stats/analgesia")
+@Api(tags = "镇痛分析",description = "统一权限前缀(stats:analyse),例如新增stats:analyse:add")
+public class BusStatsAnalyseController {
+    private final Map<StatsAnalyseEnum,CommonStats> statsMap=new EnumMap<>(StatsAnalyseEnum.class);
+
+    private final LocalBusInfusionHistoryService infusionHistoryService;
+
+    public BusStatsAnalyseController(List<CommonStats> statsList, LocalBusInfusionHistoryService infusionHistoryService) {
+        if(CollectionUtil.isNotEmpty(statsList)){
+            for (CommonStats commonStats : statsList) {
+                statsMap.merge(commonStats.getId(),commonStats,(t1,t2)->{
+                    throw new RuntimeException(String.format("统计类型【%s】服务类重复",t1.getId()));
+                });
+            }
+        }
+        this.infusionHistoryService = infusionHistoryService;
+    }
+
+    @PostMapping("/{type}")
+    @ApiOperation("统计")
+    public R stats(@RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId,@PathVariable("type")@ApiParam("统计类型 1、PCA镇痛分析 2、提示分析 3、评价分析 4、自控分析 5、输注量分析") Integer type, @RequestBody CombineQuery query){
+        if (StrUtil.isBlank(tenantId)) {
+            throw new TenantException();
+        }
+        if(query.getTimeUnit()==null){
+            throw new CustomException("时间单位不能为空");
+        }
+        StatsAnalyseEnum analyseEnum = StatsAnalyseEnum.of(type);
+        if(analyseEnum==null){
+            throw new CustomException("分析类型不能为空");
+        }
+        CommonStats commonStats=statsMap.get(analyseEnum);
+
+        List result = commonStats.queryResult(query);
+        if(CollectionUtil.isNotEmpty(result)){
+            result.sort(Comparator.comparing(CombineResult::getInfusionStartTime));
+            StatsAnalyseResult of = StatsAnalyseResult.of(commonStats.handlePie(result), commonStats.handleLine(result, query.getTimeUnit()), commonStats.handleTable(result,query.getTimeUnit()));
+            return R.success(of);
+        }
+        return R.success(StatsAnalyseResult.empty());
+
+
+    }
+}

+ 22 - 0
nb-system/src/main/java/com/nb/bus/controller/vo/CauseVo.java

@@ -0,0 +1,22 @@
+package com.nb.bus.controller.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName CauseVo.java
+ * @Description TODO
+ * @createTime 2022年05月21日 14:14:00
+ */
+@Data
+@ApiModel("报警原因")
+public class CauseVo {
+    @NotNull(message = "报警原因不能为空")
+    @ApiModelProperty("报警原因")
+    private String cause;
+}

+ 28 - 0
nb-system/src/main/java/com/nb/bus/controller/vo/ClinicEditVo.java

@@ -0,0 +1,28 @@
+package com.nb.bus.controller.vo;
+
+import com.nb.bus.entity.BusClinicEntity;
+import com.nb.bus.entity.BusDeviceManualEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName ClinicEditVo.java
+ * @Description TODO
+ * @createTime 2022年05月06日 09:29:00
+ */
+@ApiModel("编辑/新增")
+@Data
+public class ClinicEditVo {
+    @ApiModelProperty("0、无泵 1、输注 ")
+    @NotNull(message = "输注判断标识不能为空")
+    private Boolean haveDevice;
+    @ApiModelProperty("设备信息")
+    private BusDeviceManualEntity manual;
+    @ApiModelProperty("手术信息")
+    private BusClinicEntity clinic;
+}

+ 41 - 0
nb-system/src/main/java/com/nb/bus/controller/vo/ClinicStatsVo.java

@@ -0,0 +1,41 @@
+package com.nb.bus.controller.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.*;
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName ClinicStatsVo.java
+ * @Description TODO
+ * @createTime 2022年04月27日 15:46:00
+ */
+@ApiModel("临床数据统计")
+@Data
+public class ClinicStatsVo {
+    @ApiModelProperty("临床id")
+    @NotNull(message = "临床id不能为空")
+    private String clinicId;
+    @ApiModelProperty("设备id")
+    private String deviceId;
+    @ApiModelProperty("输注id")
+    private String infusionId;
+    @ApiModelProperty("是否统计流速")
+    private boolean continueDose;
+    @ApiModelProperty("是否统计追加量")
+    private boolean appendDose;
+    @ApiModelProperty("是否统计已输注量")
+    private boolean inputDose;
+    @ApiModelProperty("是否统计有效次数")
+    private boolean validCount;
+    @ApiModelProperty("是否统计无效次数")
+    private boolean inValidCount;
+
+    @ApiModelProperty("参数修改id")
+    private List<String> infusionModifyIds;
+    @ApiModelProperty("是否根据参数修改记录进行统计")
+    private boolean statsByModify;
+}

+ 24 - 0
nb-system/src/main/java/com/nb/bus/controller/vo/DeviceBindVo.java

@@ -0,0 +1,24 @@
+package com.nb.bus.controller.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.*;
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName DeviceShiftBindVO.java
+ * @Description 设备切换绑定
+ * @createTime 2022年05月05日 22:32:00
+ */
+@Data
+@ApiModel("设备绑定视图")
+public class DeviceBindVo {
+    @ApiModelProperty("需要绑定的医院")
+    private String bindTenantId;
+    @ApiModelProperty("换绑设备id(设备唯一标识)")
+    @NotNull(message = "请选择设备")
+    private List<String> deviceIds;
+}

+ 34 - 0
nb-system/src/main/java/com/nb/bus/controller/vo/DeviceRunningMockVo.java

@@ -0,0 +1,34 @@
+package com.nb.bus.controller.vo;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.nb.bus.enums.DeviceTypeEnum;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName DeviceRunningMockVo.java
+ * @Description TODO
+ * @createTime 2022年04月28日 11:52:00
+ */
+@ApiModel("Mock设备运行数据")
+@Data
+public class DeviceRunningMockVo {
+    @ApiModelProperty(value = "网络泵id",readOnly = true)
+    private String deviceId;
+
+    @ApiModelProperty(value = "病号")
+    private String patientCode;
+
+    @ApiModelProperty(value = "分包标记位",readOnly = true)
+    @JsonIgnoreProperties(allowSetters = true)
+    private String classification;
+
+    @ApiModelProperty(value = "泵类型",readOnly = true)
+    @TableField(javaType = true,updateStrategy = FieldStrategy.NEVER)
+    private DeviceTypeEnum type;
+}

+ 26 - 0
nb-system/src/main/java/com/nb/bus/controller/vo/ExecScript.java

@@ -0,0 +1,26 @@
+package com.nb.bus.controller.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName ExecScript.java
+ * @Description TODO
+ * @createTime 2022年05月21日 19:14:00
+ */
+@Data
+@ApiModel("脚本执行")
+public class ExecScript {
+    @ApiModelProperty("脚本内容")
+    @NotBlank(message = "脚本内容不能为空")
+    private String content;
+    @ApiModelProperty("输入参数")
+    private String input;
+    @ApiModelProperty("脚本类型")
+    private String type;
+}

+ 24 - 0
nb-system/src/main/java/com/nb/bus/controller/vo/GetPatientInfoVo.java

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

+ 31 - 0
nb-system/src/main/java/com/nb/bus/controller/vo/MonitorDetailVo.java

@@ -0,0 +1,31 @@
+package com.nb.bus.controller.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName MonitorDetailVo.java
+ * @Description 监控详情查看
+ * @createTime 2022年06月01日 14:02:00
+ */
+@Data
+@ApiModel
+public class MonitorDetailVo {
+    @ApiModelProperty("监控类型 true、输注监控 false、其他监控")
+    @NotNull(message = "监控类型不能为空")
+    private Boolean monitorType;
+    @ApiModelProperty("临床id,若传入infusionId,即查该次此输注的数据;若不传入infusionId,即查询病人最新的输注数据")
+    @NotNull(message = "临床id不能为空")
+    private String clinicId;
+    @ApiModelProperty("病号")
+    @NotNull(message = "病号不能为空")
+    private String patientCode;
+
+    @ApiModelProperty("输注id,当输注id存在时,以输注id为主,在输注历史查询时使用")
+    private String infusionId;
+}

+ 30 - 0
nb-system/src/main/java/com/nb/bus/controller/vo/MonitorFinishedVo.java

@@ -0,0 +1,30 @@
+package com.nb.bus.controller.vo;
+
+import com.nb.bus.service.dto.UndoDeviceConfig;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName MonitorFinishedVo.java
+ * @Description TODO
+ * @createTime 2022年04月20日 20:34:00
+ */
+@ApiModel("监控结束")
+@Data
+public class MonitorFinishedVo {
+    @ApiModelProperty("结束监控的病号-临床监护必填")
+    private List<String> patientCodes;
+
+    @ApiModelProperty("结束监控的临床号-无泵管理必填")
+    private List<String> clinicIds;
+
+    @ApiModelProperty("撤泵配置")
+    @NotNull(message = "撤泵配置")
+    private UndoDeviceConfig undo;
+}

+ 26 - 0
nb-system/src/main/java/com/nb/bus/controller/vo/NoPumpConfigVo.java

@@ -0,0 +1,26 @@
+package com.nb.bus.controller.vo;
+
+import com.nb.bus.entity.BusClinicEntity;
+import com.nb.bus.entity.BusDeviceRunningEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName NoPumpConfigVo.java
+ * @Description 无泵监护配置
+ * @createTime 2022年03月31日 20:58:00
+ */
+@Data
+@ApiModel("新增无泵监护配置")
+public class NoPumpConfigVo {
+    @ApiModelProperty("泵数据")
+    private BusDeviceRunningEntity pump;
+    @ApiModelProperty("手术信息")
+    private BusClinicEntity clinic;
+    @ApiModelProperty("医院号")
+    private String tenantId;
+
+}

+ 76 - 0
nb-system/src/main/java/com/nb/bus/entity/BusAppConfig.java

@@ -0,0 +1,76 @@
+package com.nb.bus.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.nb.bus.enums.AppEnum;
+import com.nb.common.entity.GenericEntity;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * <p>
+ * 行政区域
+ * </p>
+ *
+ * @author Kevin
+ * @since 2021-08-08
+ */
+@Data
+@TableName("sys_app_config")
+public class BusAppConfig extends GenericEntity<String> implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * app名称
+     */
+    @NotNull(message = "名称不能为空")
+    @ApiModelProperty("安装包名称")
+    private String name;
+
+
+    /**
+     * app名称
+     */
+    @NotNull(message = "安装包类型不能为空")
+    @ApiModelProperty("安装包类型")
+    private AppEnum type;
+
+    /**
+     * 存储id
+     */
+    @NotNull(message = "文件路径不能为空",groups = Insert.class)
+    @ApiModelProperty("安装包存储url")
+    private String url;
+
+    /**
+     * 版本号
+     */
+    @NotNull(message = "版本号不能为空")
+    @ApiModelProperty("安装包版本号")
+    private String version;
+
+    /**
+     * 版本号
+     */
+    @NotNull(message = "版本号不能为空")
+    @ApiModelProperty("版本号名称")
+    private String versionName;
+
+    /**
+     * 版本号
+     */
+    @NotNull(message = "版本号不能为空")
+    @ApiModelProperty("apk大小")
+    private String apkSize;
+
+    /**
+     * 版本号
+     */
+    @NotNull(message = "版本号不能为空")
+    @ApiModelProperty("apkMd5")
+    private String apkMd5;
+
+}

+ 128 - 0
nb-system/src/main/java/com/nb/bus/entity/BusClinicEntity.java

@@ -0,0 +1,128 @@
+package com.nb.bus.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
+import com.nb.bus.service.dto.FormulaDrugDomain;
+import com.nb.bus.service.dto.UndoDeviceConfig;
+import com.nb.common.entity.TenantGenericEntity;
+import com.nb.common.enums.SexEnum;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.hibernate.validator.constraints.Length;
+import java.util.*;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusClinicEntity.java
+ * @Description TODO
+ * @createTime 2022年03月21日 11:17:00
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName(value = "bus_clinic",autoResultMap = true)
+@ApiModel(value="病人临床手术信息", description="病人临床手术信息实体类")
+@ToString
+public class BusClinicEntity extends TenantGenericEntity<String,String> {
+
+    @ApiModelProperty(value = "临床手术名称")
+    @Length(max = 255,message = "临床手术名称长度不得超过255个字节")
+    private String surgeryName;
+
+    @ApiModelProperty(value = "住院号")
+    @Length(max = 255,message = "病号长度不得超过255个字节")
+    private String patientCode;
+
+    @ApiModelProperty(value = "住院号id")
+    private String patientId;
+
+    @ApiModelProperty(value = "手术开始时间")
+    private Date startTime;
+
+    @TableField
+    @ApiModelProperty("手术监护开始时间,即该临床手术后第一次上传数据时间")
+    private Date monitorStartTime;
+
+    @ApiModelProperty(value = "手术监护结束时间")
+    private Date endTime;
+
+    @ApiModelProperty(value = "asa")
+    private String asa;
+
+    @ApiModelProperty(value = "患者姓名")
+    @Length(max = 255,message = "患者姓名长度不得超过255个字节")
+    private String patientName;
+
+    @ApiModelProperty(value = "患者性别")
+    private SexEnum patientGender;
+
+    @ApiModelProperty(value = "患者年龄")
+    private Integer patientAge;
+
+    @ApiModelProperty(value = "病区")
+    @Length(max = 255,message = "病区")
+    private String ward;
+
+    @ApiModelProperty(value = "病床号")
+    @Length(max = 255,message = "病床号长度不得超过255个字节")
+    private String bedNo;
+
+    @ApiModelProperty(value = "体重")
+    @Length(max = 255,message = "体重不得超过255个字节")
+    private String weight;
+
+    @ApiModelProperty(value = "身高")
+    @Length(max = 255,message = "身高不得超过255个字节")
+    private String height;
+
+    @ApiModelProperty(value = "麻醉医生")
+    @Length(max = 255,message = "麻醉医生不得超过255个字节")
+    private String anaDoctor;
+
+    @ApiModelProperty(value = "麻醉方式")
+    private String anaType;
+
+    @ApiModelProperty(value = "镇痛方式")
+    private String analType;
+
+    @ApiModelProperty(value = "手术医生")
+    @Length(max = 255,message = "手术医生不得超过255个字节")
+    private String surgeryDoctor;
+
+    @ApiModelProperty(value = "配置人员")
+    private String configPerson;
+
+    @ApiModelProperty(value = "配方")
+    @TableField(typeHandler = FastjsonTypeHandler.class,javaType = true)
+    private FormulaDrugDomain formula;
+
+    @ApiModelProperty(value = "临床是否结束,0、未结束 1、结束 ")
+    private Boolean finished;
+
+    @TableField(fill = FieldFill.INSERT)
+    @TableLogic(value = "0",delval = "1")
+    private Integer isDelete;
+
+    @ApiModelProperty("结束管理(撤泵)配置,仅在 其他监控中使用")
+    @TableField(typeHandler = FastjsonTypeHandler.class)
+    private UndoDeviceConfig undoConfig;
+
+    //todo
+    @ApiModelProperty(value = "监护类型,1、有泵监护 0、无泵监护")
+    @JsonIgnoreProperties(allowGetters = true)
+    private Boolean monitorType;
+
+    @ApiModelProperty(value = "医嘱")
+    private String entrust;
+
+    @ApiModelProperty("该临床的最后一次评价时间")
+    private Date evalTime;
+
+}

+ 37 - 0
nb-system/src/main/java/com/nb/bus/entity/BusConAlarmEntity.java

@@ -0,0 +1,37 @@
+package com.nb.bus.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.nb.bus.enums.DeviceAlarmEnum;
+import com.nb.common.entity.TenantGenericEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.hibernate.validator.constraints.Length;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusConAlarmEntity.java
+ * @Description TODO
+ * @createTime 2022年04月14日 10:09:00
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName(value = "bus_con_alarm",autoResultMap = true)
+@ApiModel(value="常量设置的混合类", description="包括:病区、手术、ASA、镇痛、麻醉、药品分类、医嘱")
+@ToString
+public class BusConAlarmEntity extends TenantGenericEntity<String,String> {
+
+    @ApiModelProperty(value = "报警原因")
+    @Length(max = 255,message = "报警原因长度不得超过255个字节")
+    private String cause;
+
+    @ApiModelProperty(value = "备注")
+    @Length(max = 255,message = "备注长度不得超过255个字节")
+    private String remark;
+
+    @ApiModelProperty(value = "报警类型",example = "ward,surgery,asa,anaesthesia,anal,drugCate,entrust")
+    private DeviceAlarmEnum type;
+}

+ 53 - 0
nb-system/src/main/java/com/nb/bus/entity/BusConDoctor.java

@@ -0,0 +1,53 @@
+package com.nb.bus.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.nb.common.entity.TenantGenericEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.hibernate.validator.constraints.Length;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusConDoctor.java
+ * @Description TODO
+ * @createTime 2022年04月14日 10:54:00
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName(value = "bus_con_doctor",autoResultMap = true)
+@ApiModel(value="常量人员", description="包括:病区、手术、ASA、镇痛、麻醉、药品分类、医嘱")
+@ToString
+@NoArgsConstructor
+public class BusConDoctor extends TenantGenericEntity<String,String> {
+
+    @ApiModelProperty(value = "医生名称")
+    @Length(max = 255,message = "名称长度不得超过255个字节")
+    private String name;
+
+    @ApiModelProperty(value = "备注")
+    @Length(max = 255,message = "备注长度不得超过255个字节")
+    private String remark;
+
+    @ApiModelProperty(value = "是否为手术医生")
+    private Boolean surgeon;
+
+    @ApiModelProperty(value = "是否为麻醉医生")
+    private Boolean anesthetists;
+
+    @ApiModelProperty(value = "是否为配置、撤泵、评价人员")
+    private Boolean reviewer;
+
+    public BusConDoctor(String name, Boolean surgeon, Boolean anesthetists, Boolean reviewer, String remark,String tenantId) {
+        this.name = name;
+        this.remark = remark;
+        this.surgeon = surgeon;
+        this.anesthetists = anesthetists;
+        this.reviewer = reviewer;
+        this.setTenantId(tenantId);
+    }
+}

+ 48 - 0
nb-system/src/main/java/com/nb/bus/entity/BusConMixEntity.java

@@ -0,0 +1,48 @@
+package com.nb.bus.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.nb.bus.enums.ConstantMixEnum;
+import com.nb.common.entity.TenantGenericEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.hibernate.validator.constraints.Length;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusConMixEntity.java
+ * @Description TODO
+ * @createTime 2022年04月14日 10:09:00
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName(value = "bus_con_mix",autoResultMap = true)
+@ApiModel(value="常量设置的混合类", description="包括:病区、手术、ASA、镇痛、麻醉、药品分类、医嘱")
+@ToString
+@NoArgsConstructor
+public class BusConMixEntity extends TenantGenericEntity<String,String> {
+
+    @ApiModelProperty(value = "常量名称")
+    @Length(max = 255,message = "名称长度不得超过255个字节")
+    private String name;
+
+    @ApiModelProperty(value = "备注")
+    @Length(max = 255,message = "备注长度不得超过255个字节")
+    private String remark;
+
+    @ApiModelProperty(value = "混合常量类型",example = "ward,surgery,asa,anaesthesia,anal,drugCate,entrust")
+    private ConstantMixEnum type;
+
+    private String code;
+
+    public BusConMixEntity(String name, ConstantMixEnum type,String tenantId,String remark) {
+        this.name = name;
+        this.type = type;
+        this.remark=remark;
+        this.setTenantId(tenantId);
+    }
+}

+ 186 - 0
nb-system/src/main/java/com/nb/bus/entity/BusDeviceAlarmEntity.java

@@ -0,0 +1,186 @@
+package com.nb.bus.entity;
+
+import cn.hutool.json.JSONObject;
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.nb.bus.entity.common.CommonDeviceParam;
+import com.nb.bus.enums.DeviceAlarmEnum;
+import com.nb.bus.enums.DeviceStatusEnum;
+import com.nb.bus.enums.DeviceTypeEnum;
+import com.nb.bus.enums.FlowStatusEnum;
+import com.nb.common.entity.TenantGenericEntity;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.tio.utils.crypto.Md5;
+
+import java.util.Date;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusDeviceAlarmEntity.java
+ * @Description 设备报警信息
+ * @createTime 2022年04月08日 16:18:00
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName(value = "bus_device_alarm",autoResultMap = true)
+@ApiModel(value="设备报警信息", description="设备报警信息记录")
+@ToString
+public class BusDeviceAlarmEntity extends TenantGenericEntity<String,String> {
+
+    @ApiModelProperty(value = "设备唯一编码",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    private String deviceId;
+
+    @ApiModelProperty(value = "报警时间")
+    private Date uploadTime;
+
+    @ApiModelProperty(value = "报警原因")
+    private String cause;
+
+    @ApiModelProperty(value = "是否为报警信息",hidden = true)
+    @JsonIgnoreProperties
+    private Boolean alarm;
+
+    @ApiModelProperty(value = "报警记录所绑定的历史记录id,后续用于更新操作",hidden = true)
+    @JsonIgnoreProperties(allowSetters = true)
+    private String historyId;
+
+    @ApiModelProperty(value = "输注记录")
+    private String infusionId;
+
+    @ApiModelProperty(value = "设备类型")
+    private DeviceTypeEnum deviceType;
+
+    @ApiModelProperty(value = "泵运行状态",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private DeviceStatusEnum runState;
+
+    @ApiModelProperty(value = "报警信息",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private DeviceAlarmEnum alarmState;
+
+    @ApiModelProperty(value = "输注即将结束提醒",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private Boolean warnWillFinished;
+
+    @ApiModelProperty(value = "镇痛不足提醒",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private Boolean warnAnalgesicPoor;
+
+    @ApiModelProperty(value = "电量偏低提醒",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private Boolean warnLowBattery;
+
+    @ApiModelProperty(value = "加减档提示",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    private FlowStatusEnum warnFlow;
+
+    public static BusDeviceAlarmEntity parseHistory(BusDeviceHistoryEntity history){
+        BusDeviceAlarmEntity entity = new BusDeviceAlarmEntity();
+        entity.setDeviceId(history.getDeviceId());
+        entity.setUploadTime(history.getUploadTime());
+        if (history.getAlarm() != null && !DeviceAlarmEnum.None.equals(history.getAlarm())) {
+            entity.setAlarm(true);
+        }
+        entity.setHistoryId(history.getId());
+        entity.setInfusionId(history.getInfusionId());
+        entity.setRunState(history.getRunState());
+        entity.setAlarmState(history.getAlarm());
+        entity.setWarnWillFinished(history.getWarnWillFinished());
+        entity.setWarnAnalgesicPoor(history.getWarnAnalgesicPoor());
+        entity.setWarnLowBattery(history.getWarnLowBattery());
+        entity.setWarnFlow(history.getWarnFlow());
+        entity.setTenantId(history.getTenantId());
+        entity.setDeviceType(history.getType());
+        return entity;
+    }
+
+    public static BusDeviceAlarmEntity parseRunning(BusInfusionHistoryEntity source){
+        BusDeviceAlarmEntity entity = new BusDeviceAlarmEntity();
+        entity.setDeviceId(source.getDeviceId());
+        entity.setUploadTime(source.getLastUploadTime());
+        if (source.getAlarm() != null && !DeviceAlarmEnum.None.equals(source.getAlarm())) {
+            entity.setAlarm(true);
+        }
+        entity.setHistoryId(source.getId());
+        entity.setInfusionId(source.getId());
+        entity.setRunState(source.getRunState());
+        entity.setAlarmState(source.getAlarm());
+        entity.setWarnWillFinished(source.getWarnWillFinished());
+        entity.setWarnAnalgesicPoor(source.getWarnAnalgesicPoor());
+        entity.setWarnLowBattery(source.getWarnLowBattery());
+        entity.setWarnFlow(source.getWarnFlow());
+        entity.setTenantId(source.getTenantId());
+        entity.setDeviceType(source.getType());
+        return entity;
+    }
+
+    public String signParm(){
+        JSONObject param = new JSONObject(true);
+        param.putOpt("runState",String.valueOf(this.getRunState()));
+        param.putOpt("alarmState",String.valueOf(this.getAlarmState()));
+        param.putOpt("warnWillFinished",String.valueOf(this.getWarnWillFinished()));
+        param.putOpt("warnAnalgesicPoor",String.valueOf(this.getWarnAnalgesicPoor()));
+        param.putOpt("warnLowBattery",String.valueOf(this.getWarnLowBattery()));
+        param.putOpt("warnFlow",String.valueOf(this.getWarnFlow()));
+        return Md5.getMD5(param.toString());
+    }
+
+    public static boolean alarmOrWarn(CommonDeviceParam history){
+        if (history.getAlarm() != null && !DeviceAlarmEnum.None.equals(history.getAlarm())) {
+            return true;
+        }
+        if (Boolean.TRUE.equals(history.getWarnAnalgesicPoor())) {
+            return true;
+        }
+        if (Boolean.TRUE.equals(history.getWarnLowBattery())) {
+            return true;
+        }
+        if (Boolean.TRUE.equals(history.getWarnWillFinished())) {
+            return true;
+        }
+        return history.getWarnFlow()!=null;
+    }
+
+
+    public static BusDeviceAlarmEntity noSignalOf(String deviceId,String infusionId,String historyId,String tenantId,Date uploadTIme){
+        BusDeviceAlarmEntity result = of(deviceId,infusionId,historyId,tenantId,uploadTIme);
+        result.setRunState(DeviceStatusEnum.NoSignal);
+        return result;
+    }
+
+    public static BusDeviceAlarmEntity lowInfusion(String deviceId,String infusionId,String historyId,String tenantId,Date uploadTIme){
+        BusDeviceAlarmEntity result = of(deviceId,infusionId,historyId,tenantId,uploadTIme);
+        result.setWarnFlow(FlowStatusEnum.Lowest);
+        return result;
+    }
+
+    public static BusDeviceAlarmEntity insufficient(String deviceId,String infusionId,String historyId,String tenantId,Date uploadTIme){
+        BusDeviceAlarmEntity result = of(deviceId,infusionId,historyId,tenantId,uploadTIme);
+        result.setWarnAnalgesicPoor(true);
+        return result;
+    }
+
+    private static BusDeviceAlarmEntity of(String deviceId,String infusionId,String historyId,String tenantId,Date uploadTIme){
+        BusDeviceAlarmEntity result = new BusDeviceAlarmEntity();
+        result.setRunState(DeviceStatusEnum.Waiting);
+        result.setInfusionId(infusionId);
+        result.setTenantId(tenantId);
+        result.setUploadTime(uploadTIme);
+        result.setHistoryId(historyId);
+        result.setAlarm(false);
+        result.setAlarmState(DeviceAlarmEnum.None);
+        result.setDeviceId(deviceId);
+        result.setWarnLowBattery(false);
+        result.setWarnFlow(FlowStatusEnum.None);
+        result.setWarnAnalgesicPoor(false);
+        result.setWarnWillFinished(false);
+        return result;
+    }
+}

+ 112 - 0
nb-system/src/main/java/com/nb/bus/entity/BusDeviceEntity.java

@@ -0,0 +1,112 @@
+package com.nb.bus.entity;
+
+import com.aliyuncs.iot.model.v20180120.QueryDeviceDetailResponse;
+import com.aliyuncs.iot.model.v20180120.QueryDeviceResponse;
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
+import com.nb.aliyun.utils.EnumUtils;
+import com.nb.bus.bean.AliIotConfig;
+import com.nb.bus.enums.DeviceStatusEnum2;
+import com.nb.bus.enums.DeviceTypeEnum;
+import com.nb.common.config.mybatis.TenantNameHandler;
+import com.nb.common.entity.TenantGenericEntity;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.hibernate.validator.constraints.Length;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusDeviceEntity.java
+ * @Description TODO
+ * @createTime 2022年03月21日 11:17:00
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName(value = "bus_device",autoResultMap = true)
+@ApiModel(value="设备经销商管理", description="设备与医院关系管理")
+@ToString
+public class BusDeviceEntity extends TenantGenericEntity<String,String> {
+
+
+    @ApiModelProperty(value = "设备唯一id")
+    private String deviceId;
+
+
+    @ApiModelProperty(value = "mqtt通道信息")
+    @JsonIgnore
+    private String mqttConnInfo;
+
+    @ApiModelProperty(value = "设备别名")
+    @TableField(updateStrategy = FieldStrategy.IGNORED)
+    @Length(max = 255,message = "设备别名不得超过255个字符")
+    private String alias;
+
+    @ApiModelProperty(value = "输注id")
+    @JsonIgnore
+    private String infusionId;
+
+    @ApiModelProperty(value = "设备类型")
+    @Deprecated
+    @Length(max = 255,message = "设备类型不得超过255个字符")
+    private DeviceTypeEnum type;
+
+    @ApiModelProperty(value = "第三方平台返回配置")
+    @TableField(typeHandler = FastjsonTypeHandler.class,updateStrategy = FieldStrategy.DEFAULT)
+    private AliIotConfig config;
+
+
+    @ApiModelProperty(value = "sim卡卡号")
+    @Length(max = 255,message = "sim卡卡号不得超过255个字符")
+    private String simIccid;
+
+    @ApiModelProperty(value = "移动网络运营商")
+    @Length(max = 255,message = "移动网络运营商不得超过255个字符")
+    private String simMno;
+
+    @ApiModelProperty(value = "是否启用,0、不启用 1、启用 ")
+    private Boolean enable;
+
+
+    @TableField(fill = FieldFill.INSERT)
+    @TableLogic(value = "0",delval = "1")
+    private Integer isDelete;
+
+    @ApiModelProperty(value = "设备在线状态,0未激活,1在线,2离线",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    private DeviceStatusEnum2 status;
+
+    @TableField(value = "tenant_id",typeHandler = TenantNameHandler.class,updateStrategy = FieldStrategy.NEVER,insertStrategy = FieldStrategy.NEVER)
+    private String tenantName;
+
+    /**
+     * @author 龙三郎
+     * 根据阿里云返回的数据更新设备对象
+     * @param data 由queryDeviceDetail接口返回的数据
+     */
+    public void updateFields(QueryDeviceDetailResponse.Data data) {
+        this.deviceId = data.getDeviceName();
+        this.config.setProductKey(data.getProductKey());
+        this.config.setIotId(data.getIotId());
+        this.config.setDeviceSecret(data.getDeviceSecret());
+        this.config.setDeviceName(data.getDeviceName());
+        this.setStatus(EnumUtils.getDeviceStatusEnum2(data.getStatus()));
+    }
+
+    /**
+     * @author 龙三郎
+     * 根据设备查询接口返回的数据,更新设备对象
+     * @param item
+     */
+    public void updateFields(QueryDeviceResponse.DeviceInfo item) {
+        this.deviceId = item.getDeviceName();
+        this.config.setProductKey(item.getProductKey());
+        this.config.setIotId(item.getIotId());
+        this.config.setDeviceSecret(item.getDeviceSecret());
+        this.config.setDeviceName(item.getDeviceName());
+        this.setStatus(EnumUtils.getDeviceStatusEnum2(item.getDeviceStatus()));
+    }
+}

+ 62 - 0
nb-system/src/main/java/com/nb/bus/entity/BusDeviceHistoryEntity.java

@@ -0,0 +1,62 @@
+package com.nb.bus.entity;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.nb.bus.entity.common.CommonDeviceParam;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * @Author lifang
+ * @Date 10:52 2022/4/13
+ * @Description 设备历史运行数据记录
+ * @Param
+ * @return
+ **/
+
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName(value = "bus_device_history",autoResultMap = true)
+@ApiModel(value="设备历史运行数据", description="设备历史运行数据记录")
+public class BusDeviceHistoryEntity extends CommonDeviceParam<String,String> {
+
+    @ApiModelProperty(value = "公共-此次输注过程中到此为止的总追加量")
+    private BigDecimal totalAppendDose;
+
+    @ApiModelProperty(value = "输注记录id")
+    @JsonIgnoreProperties
+    private String infusionId;
+
+    @ApiModelProperty(value = "输注修改记录id")
+    @JsonIgnoreProperties
+    private String infusionModifyId;
+
+    @ApiModelProperty(value = "是否为主泵数据, 0、副泵 1、主泵(即当前临床绑定的泵)")
+    @JsonIgnoreProperties
+    private Boolean master;
+
+    @ApiModelProperty(value = "数据上传时间",accessMode = ApiModelProperty.AccessMode.READ_ONLY,hidden = true)
+    @JsonIgnoreProperties(allowSetters = true)
+    private Date uploadTime;
+
+
+    public static BusDeviceHistoryEntity parseRunningInfo(BusDeviceRunningEntity running){
+        BusDeviceHistoryEntity entity = new BusDeviceHistoryEntity();
+        BeanUtil.copyProperties(running,entity);
+        entity.setPatientCode(running.getPatientCode());
+        entity.setInfusionId(running.getInfusionId());
+        entity.setInfusionModifyId(running.getInfusionModifyId());
+        entity.setUploadTime(running.getUploadTime());
+        entity.setMaster(running.getMaster());
+        entity.setTenantId(running.getTenantId());
+        return entity;
+    }
+}

+ 86 - 0
nb-system/src/main/java/com/nb/bus/entity/BusDeviceManualEntity.java

@@ -0,0 +1,86 @@
+package com.nb.bus.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
+import com.nb.bus.enums.DeviceManualEnum;
+import com.nb.common.entity.GenericEntity;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.*;
+import java.math.BigDecimal;
+import java.util.Map;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusDocEntity.java
+ * @Description
+ * @createTime 2022年04月09日 16:12:00
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName(value = "bus_device_manual",autoResultMap = true)
+@ApiModel(value="手动输录设备管理", description="手动输录设备管理")
+@ToString
+public class BusDeviceManualEntity extends GenericEntity<String> {
+    @ApiModelProperty("设备类型")
+    private DeviceManualEnum type;
+
+    @ApiModelProperty("当设备类型为【其他】时,用户自定义输入(key,value)【(参数名称,参数值)】即可")
+    @TableField(typeHandler = FastjsonTypeHandler.class)
+    private Map<String,Object> config;
+
+    @ApiModelProperty(value = "公共参数-总量",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @Max(value = 999,message = "总量最大值不得超过999")
+    @Min(value = 0,message ="总量最小值不得超过0" )
+    private Integer totalDose;
+
+    @ApiModelProperty(value = "临床id",hidden = true)
+    @JsonIgnore
+    private String clinicId;
+
+    @ApiModelProperty(value = "公共参数-自控锁时",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    private Integer selfControlLockTime;
+
+    @ApiModelProperty(value = "公共参数-自控量",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    private BigDecimal selfControlDose;
+
+    @ApiModelProperty(value = "机械泵、电子脉冲泵-持续量",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @DecimalMax(value = "50",message = "持续给液量最大值不得超过50")
+    @DecimalMin(value = "0",message ="持续给液量最小值不得超过0" )
+    private BigDecimal continueDose;
+
+
+    @ApiModelProperty(value = "电子泵、电子脉冲泵-首次量",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @Max(value = 50,message = "首次量最大值不得超过50")
+    @Min(value = 0,message ="首次量最小值不得超过0" )
+    private Integer firstDose;
+
+
+    @ApiModelProperty(value = "电子脉冲泵-脉冲首次锁时",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @Max(value = 60,message = "脉冲首次锁时最大值不得超过60")
+    @Min(value = 0,message ="脉冲首次锁时最小值不得超过0" )
+    private Integer firstLockTime;
+
+    @ApiModelProperty(value = "电子脉冲泵-脉冲量",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @Max(value = 20,message = "脉冲量最大值不得超过20")
+    @Min(value = 0,message ="脉冲量最小值不得超过0" )
+    private Integer pulseDose;
+
+    @ApiModelProperty(value = "电子脉冲泵-脉冲锁时",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @Max(value = 90,message = "脉冲锁时最大值不得超过90")
+    @Min(value = 30,message ="脉冲锁时最小值不得超过30" )
+    private Integer pulseLockTime;
+
+    @ApiModelProperty(value = "电子脉冲泵、电子泵-极限量",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    @DecimalMax(value = "90",message = "PCA追加量最大值不得超过90")
+    @DecimalMin(value = "0",message ="PCA追加量最小值不得超过0" )
+    private BigDecimal maxDose;
+
+}

+ 207 - 0
nb-system/src/main/java/com/nb/bus/entity/BusDeviceRunningEntity.java

@@ -0,0 +1,207 @@
+package com.nb.bus.entity;
+
+
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.nb.aliyun.utils.*;
+import com.nb.bus.entity.common.CommonDeviceParam;
+import com.nb.bus.enums.DeviceTypeEnum;
+import com.nb.common.enums.SexEnum;
+import com.fasterxml.jackson.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.Optional;
+
+/**
+ * @Author lifang
+ * @Date 16:02 2022/4/8
+ * @Description
+ * @Param
+ * @return
+ **/
+@ToString
+@Data
+@EqualsAndHashCode(callSuper = true)
+@Accessors(chain = true)
+@ApiModel(value="设备运行状态", description="设备运行状态")
+public class BusDeviceRunningEntity extends CommonDeviceParam<String,String> {
+
+    @ApiModelProperty(value = "公共-此次输注过程中到此为止的总追加量")
+    private BigDecimal totalAppendDose;
+
+    @ApiModelProperty(value = "输注记录")
+    private String infusionId;
+
+    @ApiModelProperty(value = "临床号")
+    private String clinicId;
+
+    /***************临床固定数据*****************/
+    @ApiModelProperty(value = "病人名称")
+    private String patientName;
+
+    @ApiModelProperty(value = "病人性别")
+    private SexEnum patientSex;
+
+    /***************临床固定数据*****************/
+
+    @ApiModelProperty(value = "设备别名")
+    private String alias;
+
+    @ApiModelProperty(value = "输注开始时间,即本次运行开机时间",accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+    private Date startTime;
+
+    @ApiModelProperty(value = "是否已撤泵,0、未撤泵1、已撤泵")
+    @JsonIgnoreProperties(allowSetters = true)
+    private Boolean isUndo;
+
+    @ApiModelProperty(value = "备注")
+    private String remark;
+
+    @ApiModelProperty(value = "监护类型,1、有泵监护 0、无泵监护")
+    @JsonIgnoreProperties
+    private Boolean monitorType;
+
+    @ApiModelProperty(value = "是否为主泵数据, 0、副泵 1、主泵(即当前临床绑定的泵)")
+    @JsonIgnoreProperties(allowSetters = true)
+    private Boolean master;
+
+
+    @ApiModelProperty(value = "数据上传时间",accessMode = ApiModelProperty.AccessMode.READ_ONLY,hidden = true)
+    @JsonIgnoreProperties(allowSetters = true)
+    private Date uploadTime;
+
+    /**
+     * 当前运行状态所绑定的输注修改记录id,只在设备上传解析过程中使用,其他地方无用处
+     */
+    @TableField(exist = false)
+    @JsonIgnoreProperties
+    private String infusionModifyId;
+
+    /**
+     * 当前运行状态所绑定的历史记录id,只在设备上传解析过程中使用,其他地方无用处
+     */
+    @TableField(exist = false)
+    @JsonIgnoreProperties
+
+    private String historyId;
+    @TableField(exist = false)
+    @JsonIgnoreProperties
+    private Date modifyTime;
+    /**
+     * 当前运行状态是否开启了新的输注,只在设备上传解析过程中使用,其他地方无用处
+     */
+    @TableField(exist = false)
+    @JsonIgnoreProperties
+    private boolean newInfusion;
+    /**
+     * 当前运行状态输注信息是否发生了变化,只在设备上传解析过程中使用,其他地方无用处
+     */
+    @TableField(exist = false)
+    @JsonIgnoreProperties
+    private boolean infusionModify;
+
+    /**
+     *当设备输注过程已撤泵被重新开启时,只在设备上传解析过程中使用,其他地方无用处
+     */
+    @TableField(exist = false)
+    @JsonIgnoreProperties
+    private boolean resetUndo;
+    /**
+     * 患者id
+     */
+    @TableField(exist = false)
+    @JsonIgnoreProperties
+    private String patientId;
+    /**
+     * 重新开启临床信息
+     */
+    @TableField(exist = false)
+    @JsonIgnoreProperties
+    private boolean resetClinic;
+    /**
+     * 格式化之后的住院号
+     */
+    @TableField(exist = false)
+    @JsonIgnoreProperties
+    private String formatPatientCode;
+    /**
+     * @author 龙三郎
+     * 根据阿里云传回数据的items,设置输注的属性
+     * @param deviceName
+     * @param items
+     */
+    public void updateFieldsByItems(String deviceName, Items items) {
+        // 属性上传时间
+        this.setUploadTime(items.getDate());
+
+        this.setDeviceId(deviceName);
+        this.setClassification( items.getString(PumpParams.classification));
+        this.setDataNumber(items.getInteger(PumpParams.dataNumber));
+        this.setPatientCode( items.getString(PumpParams.patientCode));
+        this.setWard(items.getString(PumpParams.ward));
+        this.setBedNo(items.getString(PumpParams.bedNo));
+        this.setTotalDose( items.getInteger(PumpParams.totalDose));
+        this.setInputDose(items.getBigDecimal(PumpParams.finishedDose));
+        this.setRemainDose(BigDecimal.valueOf(this.getTotalDose()).subtract(this.getInputDose()));
+        this.setFirstDose( items.getInteger(PumpParams.firstDosis));
+        this.setMaxDose(items.getBigDecimal(PumpParams.maxDose));
+        this.setAppendDose(items.getBigDecimal(PumpParams.singleDosis));
+        this.setAppendLockTime(items.getBigDecimal(PumpParams.lockTime));
+        this.setContinueDose(items.getBigDecimal(PumpParams.flow));
+        this.setType( DeviceTypeUtils.getDeviceType(items.getInteger(PumpParams.pumpType)));
+        this.setPcaValidCount(items.getInteger(PumpParams.pcaValid));
+        this.setPcaInvalidCount(items.getInteger(PumpParams.pcaInvalid));
+        this.setPcaTotalCount(this.getPcaInvalidCount()+this.getPcaValidCount());
+        this.setElectricQuantity(items.getInteger(PumpParams.electricQuantity));
+        this.setRunState(DeviceRunStatusUtils.getRunStatus(items.getInteger(PumpParams.runStatus)));
+        this.setAlarm(DeviceAlarmUtils.getAlarm(Optional.ofNullable(items.getInteger(PumpParams.alarmStatus)).orElse(0)));
+        // 预报
+        // 镇痛不足预报
+        this.setWarnAnalgesicPoor(Optional.ofNullable(items.getBoolean(PumpParams.warnAnalgesicPoor)).orElse(false));
+        // 电量偏低预报
+        this.setWarnLowBattery(Optional.ofNullable(items.getBoolean(PumpParams.warnLowBattery)).orElse(false));
+        // 输液将结束预报
+        this.setWarnWillFinished(Optional.ofNullable(items.getBoolean(PumpParams.warnWillFinished)).orElse(false));
+
+        // 设置脉冲泵参数
+        if (this.getType() == DeviceTypeEnum.pulse){
+            // 脉冲锁时
+            this.setPulseLockTime(items.getInteger(PumpParams.pulseLockTime));
+            // 首次锁时
+            this.setPulseFirstLockTime(items.getInteger(PumpParams.firstLockTime));
+            // 脉冲量
+            this.setPulseDose(items.getInteger(PumpParams.pulseDose));
+        }else if (this.getType() == DeviceTypeEnum.intelligent){
+            // 设置智能泵参数
+            // 重设参数标识
+
+            // 减档时间
+            this.setFlowDownCycle(items.getBigDecimal(PumpParams.flowDownTime));
+            // 加档时间
+            this.setFlowUpCycle(items.getBigDecimal(PumpParams.flowUpTime));
+            // 触发加档pca有效次数
+            this.setFlowCount(items.getBigDecimal(PumpParams.flowUpPcaValid));
+            // 加减档百分比
+            this.setFlowAdjustRate(items.getBigDecimal(PumpParams.flowAdjuseRate));
+            // 减档下限
+            this.setFlowDownLimit(items.getBigDecimal(PumpParams.minFlow));
+            // 加档上限
+            this.setFlowUpLimit(items.getBigDecimal(PumpParams.maxFlow));
+
+            // 加减档预报
+            this.setWarnFlow(WarnFlowUtils.getAlarm(items.getInteger(PumpParams.warnFlow)));
+        }
+
+
+    }
+
+
+
+}

+ 32 - 0
nb-system/src/main/java/com/nb/bus/entity/BusDocEntity.java

@@ -0,0 +1,32 @@
+package com.nb.bus.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.nb.common.entity.GenericEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName BusDocEntity.java
+ * @Description TODO
+ * @createTime 2022年04月09日 16:12:00
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName(value = "bus_doc",autoResultMap = true)
+@ApiModel(value="医院宣教文档管理", description="宣教文档管理")
+@ToString
+public class BusDocEntity extends GenericEntity<String> {
+    @ApiModelProperty("文档内容")
+    @NotBlank(message = "文档内容不能为空")
+    private String content;
+
+    @ApiModelProperty("文档分类")
+    private String type;
+}

+ 38 - 0
nb-system/src/main/java/com/nb/bus/entity/BusDrugEntity.java

@@ -0,0 +1,38 @@
+package com.nb.bus.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.nb.common.entity.TenantGenericEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.hibernate.validator.constraints.Length;
+
+/**
+ * @author zsl
+ * @version 1.0.0
+ * @ClassName BusDrugEntity.java
+ * @Description
+ * @createTime 2022/4/189:22
+ */
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName(value = "bus_drug",autoResultMap = true)
+@ApiModel(value="药品管理", description="药品管理实体类")
+@ToString
+public class BusDrugEntity extends TenantGenericEntity<String,String> {
+    @ApiModelProperty(value = "药品名称")
+    @Length(max = 255,message = "药品名称长度不得超过255个字节")
+    private String name;
+
+    @ApiModelProperty(value = "药品类型")
+    @Length(max = 255,message = "药品类型不得超过255个字符")
+    private String type;
+
+    @ApiModelProperty(value = "药品单位")
+    @Length(max = 255,message = "药品单位不得超过255个字符")
+    private String unit;
+
+}

+ 131 - 0
nb-system/src/main/java/com/nb/bus/entity/BusEvaluationEntity.java

@@ -0,0 +1,131 @@
+package com.nb.bus.entity;
+import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import com.nb.common.entity.TenantGenericEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author fanfan
+ * @since 2020-07-03
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("bus_evaluation")
+@ApiModel(value="评价得分", description="评价得分实体类")
+public class BusEvaluationEntity extends TenantGenericEntity<String,String> {
+
+    @ApiModelProperty(value = "病号")
+    @Length(max = 255,message = "病号长度不得超过255个字节")
+    public String patientId;
+
+    @ApiModelProperty(value = "病号")
+    @Length(max = 255,message = "病号长度不得超过255个字节")
+    public String patientCode;
+
+    @ApiModelProperty(value = "输注id,输注监控专用")
+    private String infusionId;
+
+    @ApiModelProperty(value = "临床号,无泵专用")
+    @NotNull(message = "临床id不能为空",groups = Insert.class)
+    @Length(max = 255,message = "临床号长度不得超过255个字节")
+    private String clinicId;
+
+    @ApiModelProperty(value = "泵号")
+    @Length(max = 50,message = "泵号长度不得超过50个字节")
+    private String deviceId;
+
+    @ApiModelProperty(value = "疼痛评分静止")
+    private Integer statics;
+
+    @ApiModelProperty(value = "疼痛评分活动")
+    private Integer activity;
+
+    @ApiModelProperty(value = "镇静评分")
+    private Integer calm;
+
+    @ApiModelProperty(value = "左上肢")
+    private Integer leftArm;
+
+    @ApiModelProperty(value = "左下肢")
+    private Integer leftLeg;
+
+    @ApiModelProperty(value = "右上肢")
+    private Integer rightArm;
+
+    @ApiModelProperty(value = "右下肢")
+    private Integer rightLeg;
+
+    @ApiModelProperty(value = "恶心呕吐")
+    private Integer nauseaVomit;
+
+    @ApiModelProperty(value = "瘙痒")
+    private Integer itch;
+
+    @ApiModelProperty(value = "眩晕")
+    private Integer vertigo;
+
+    @ApiModelProperty(value = "咽喉疼痛")
+    private Integer soreThroat;
+
+    @ApiModelProperty(value = "尿潴留")
+    private Integer uroschesis;
+
+    @ApiModelProperty(value = "呼吸抑制")
+    private Integer breathDepression;
+
+    @ApiModelProperty(value = "声音嘶哑")
+    private Integer hoarseness;
+
+    @ApiModelProperty(value = "认知障碍")
+    private Integer cognitionObstacle;
+
+    @ApiModelProperty(value = "其他")
+    @Length(max = 255,message = "其他长度不得超过255个字节")
+    private String other;
+
+    @ApiModelProperty(value = "满意度")
+    private Integer satisfaction;
+
+
+    @ApiModelProperty(value = "评价时间")
+    @NotNull(groups = Insert.class,message = "评价时间不可为空")
+    private Date evaluateTime;
+
+    @ApiModelProperty(value = "评价人")
+    @NotNull(groups = Insert.class,message = "评价人不可为空")
+    @Length(max = 255,message = "评价人长度不得超过255个字节")
+    private String evaluator;
+
+    @ApiModelProperty(value = "收缩压")
+    private BigDecimal  shrinkPressure;
+
+
+    @ApiModelProperty(value = "舒张压")
+    private BigDecimal diastensPressure;
+
+    @ApiModelProperty(value = "心率")
+    private BigDecimal heartRate;
+
+    @ApiModelProperty(value = "胎心")
+    private BigDecimal fetalHeartRate;
+
+    @ApiModelProperty(value = "呼吸频率")
+    private BigDecimal breathRate;
+
+    @ApiModelProperty(value = "血氧饱和度")
+    private BigDecimal bloodOxygenSaturation;
+}

+ 39 - 0
nb-system/src/main/java/com/nb/bus/entity/BusFormulaEntity.java

@@ -0,0 +1,39 @@
+package com.nb.bus.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
+import com.nb.bus.service.dto.FormulaDrugDetailDomain;
+import com.nb.common.entity.TenantGenericEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.hibernate.validator.constraints.Length;
+import java.util.*;
+/**
+ * @author zsl
+ * @version 1.0.0
+ * @ClassName BusFormulaEntity.java
+ * @Description
+ * @createTime 2022/4/1817:30
+ */
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@TableName(value = "bus_formula",autoResultMap = true)
+@ApiModel(value="配方管理", description="配方管理实体类")
+@ToString
+public class BusFormulaEntity extends TenantGenericEntity<String,String> {
+
+    @ApiModelProperty(value = "配方名称")
+    @Length(max = 255,message = "配方名称长度不得超过255个字节")
+    private String name;
+
+    @ApiModelProperty(value = "配方内容")
+    @Length(max = 2048,message = "配方内容不得超过2048个字符")
+    @TableField(typeHandler = FastjsonTypeHandler.class,javaType = true)
+    private List<FormulaDrugDetailDomain> content;
+
+}

Неке датотеке нису приказане због велике количине промена