Просмотр исходного кода

add 新增输注分析
fix 修复已知bug

A17404李放 3 лет назад
Родитель
Сommit
b1aa034b3b
36 измененных файлов с 2154 добавлено и 96 удалено
  1. 0 4
      coffee-common/src/main/java/com/coffee/common/entity/TenantGenericEntity.java
  2. 14 0
      coffee-common/src/main/java/com/coffee/common/exception/TenantException.java
  3. 5 0
      coffee-common/src/main/java/com/coffee/common/result/ResultCode.java
  4. 9 0
      coffee-framework/src/main/java/com/coffee/framework/web/exception/GlobalExceptionHandler.java
  5. 52 46
      coffee-system/src/main/java/com/coffee/bus/controller/BusClinicController.java
  6. 1 1
      coffee-system/src/main/java/com/coffee/bus/controller/BusDeviceHistoryController.java
  7. 55 10
      coffee-system/src/main/java/com/coffee/bus/controller/BusStatsAnalyseController.java
  8. 2 6
      coffee-system/src/main/java/com/coffee/bus/controller/BusStatsReportController.java
  9. 1 0
      coffee-system/src/main/java/com/coffee/bus/entity/BusClinicEntity.java
  10. 4 4
      coffee-system/src/main/java/com/coffee/bus/enums/DeviceTypeEnum.java
  11. 48 0
      coffee-system/src/main/java/com/coffee/bus/enums/StatsAnalyseEnum.java
  12. 50 0
      coffee-system/src/main/java/com/coffee/bus/enums/StatsTimeUnit.java
  13. 21 1
      coffee-system/src/main/java/com/coffee/bus/mapper/BusInfusionHistoryMapper.java
  14. 10 5
      coffee-system/src/main/java/com/coffee/bus/service/LocalBusInfusionHistoryService.java
  15. 2 0
      coffee-system/src/main/java/com/coffee/bus/service/dto/ClinicAnalClinicRecord.java
  16. 24 0
      coffee-system/src/main/java/com/coffee/bus/service/dto/CombineAlarmResult.java
  17. 3 0
      coffee-system/src/main/java/com/coffee/bus/service/dto/CombineQuery.java
  18. 5 0
      coffee-system/src/main/java/com/coffee/bus/service/dto/CombineResult.java
  19. 104 0
      coffee-system/src/main/java/com/coffee/bus/stats/CommonStats.java
  20. 281 0
      coffee-system/src/main/java/com/coffee/bus/stats/analyse/AlarmStatsAnalyse.java
  21. 251 0
      coffee-system/src/main/java/com/coffee/bus/stats/analyse/AnalStatsAnalyse.java
  22. 11 0
      coffee-system/src/main/java/com/coffee/bus/stats/analyse/EvalStatsAnalyse.java
  23. 224 0
      coffee-system/src/main/java/com/coffee/bus/stats/analyse/InfusionDoseStatsAnalyse.java
  24. 190 0
      coffee-system/src/main/java/com/coffee/bus/stats/analyse/PcaStatsAnalyse.java
  25. 200 0
      coffee-system/src/main/java/com/coffee/bus/stats/entity/AlarmTotalPieResult.java
  26. 48 0
      coffee-system/src/main/java/com/coffee/bus/stats/entity/LineResult.java
  27. 42 0
      coffee-system/src/main/java/com/coffee/bus/stats/entity/PieResult.java
  28. 20 0
      coffee-system/src/main/java/com/coffee/bus/stats/entity/StatsAnalyseResult.java
  29. 42 0
      coffee-system/src/main/java/com/coffee/bus/stats/entity/StatsColumn.java
  30. 27 0
      coffee-system/src/main/java/com/coffee/bus/stats/entity/TableResult.java
  31. 18 0
      coffee-system/src/main/java/com/coffee/bus/stats/enums/PieEnum.java
  32. 59 0
      coffee-system/src/main/java/com/coffee/bus/stats/report/GenderStatsReport.java
  33. 5 4
      coffee-system/src/main/java/com/coffee/bus/websocket/listener/DeviceInfoListener.java
  34. 7 6
      coffee-system/src/main/resources/mapper/bus/BusDeviceMapper.xml
  35. 311 1
      coffee-system/src/main/resources/mapper/bus/BusInfusionHistoryMapper.xml
  36. 8 8
      coffee-system/src/main/resources/mapper/bus/BusPatientMapper.xml

+ 0 - 4
coffee-common/src/main/java/com/coffee/common/entity/TenantGenericEntity.java

@@ -27,8 +27,4 @@ public abstract class TenantGenericEntity<PK,TN> extends GenericEntity<PK> {
     @Setter
     @JsonSerialize(using = ToStringSerializer.class)
     private TN tenantId;
-
-//    @Getter
-//    @TableField(value = "tenant_id",insertStrategy = FieldStrategy.NEVER,updateStrategy = FieldStrategy.NEVER,typeHandler = TenantNameHandler.class)
-//    private String tenantName;
 }

+ 14 - 0
coffee-common/src/main/java/com/coffee/common/exception/TenantException.java

@@ -0,0 +1,14 @@
+package com.coffee.common.exception;
+
+import lombok.Data;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName TenantException.java
+ * @Description TODO
+ * @createTime 2022年06月06日 23:07:00
+ */
+@Data
+public class TenantException extends RuntimeException {
+}

+ 5 - 0
coffee-common/src/main/java/com/coffee/common/result/ResultCode.java

@@ -93,6 +93,11 @@ public enum ResultCode implements IResultCode {
      * 登录超时,请重新登录
      */
     TOKEN_ERROR(HttpServletResponse.SC_REQUEST_TIMEOUT, "登录超时,请重新登录"),
+
+    /**
+     * 登录超时,请重新登录
+     */
+    TENANT_NONE(5001, "请选择医院"),
     ;
 
     /**

+ 9 - 0
coffee-framework/src/main/java/com/coffee/framework/web/exception/GlobalExceptionHandler.java

@@ -6,6 +6,7 @@ import cn.dev33.satoken.exception.NotRoleException;
 import com.coffee.common.exception.CustomException;
 import com.coffee.common.exception.DemoModeException;
 import com.coffee.common.exception.ScriptException;
+import com.coffee.common.exception.TenantException;
 import com.coffee.common.result.R;
 import com.coffee.common.result.ResultCode;
 import lombok.extern.slf4j.Slf4j;
@@ -45,6 +46,14 @@ public class GlobalExceptionHandler {
         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);

+ 52 - 46
coffee-system/src/main/java/com/coffee/bus/controller/BusClinicController.java

@@ -49,6 +49,7 @@ public class BusClinicController {
     private final DeviceRegistry deviceRegistry;
     private final LocalBusDeviceManualService deviceManualService;
     private final LocalBusPatientService patientService;
+    private final LocalBusHospitalService hospitalService;
     @PostMapping("/stats")
     @ApiOperation(value = "临床过程中的数据记录",notes = "权限【无】")
     public R<ClinicStatsReturnResult> stats(@RequestBody@Validated ClinicStatsVo statsVo) {
@@ -67,62 +68,67 @@ public class BusClinicController {
     }
 
 
-    @PostMapping("/anal/record/{clinicId}")
+    @PostMapping("/anal/record/{clinicId}/{after}")
     @SaCheckPermission("bus:clinic:query")
     @ApiOperation(value = "查询临床信息的镇痛访视记录单",notes = "权限【bus:clinic:query】")
-    public R<ClinicAnalRecordResult> analRecord(@PathVariable("clinicId") String clinicId,@RequestAttribute("tenantId") String tenantId){
+    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(ClinicAnalClinicRecord.parse(clinic));
-
-        //填充评价记录
-        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));
-
-            result.fillUndoInfo(infusionHistories);
-
-            //添加设备别名
-            Map<String, List<BusInfusionHistoryEntity>> infusionByDeviceId = infusionHistories.stream().collect(Collectors.groupingBy(BusInfusionHistoryEntity::getDeviceId));
-            infusionByDeviceId.forEach((deviceId,infusions)->{
-                String alias = deviceRegistry.getOperator(deviceId).getAlias();
-                infusions.forEach(infusion->infusion.setAlias(alias));
-            });
-
-            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));
-            //标记输注
-            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));
+        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));
+
+                result.fillUndoInfo(infusionHistories);
+
+                //添加设备别名
+                Map<String, List<BusInfusionHistoryEntity>> infusionByDeviceId = infusionHistories.stream().collect(Collectors.groupingBy(BusInfusionHistoryEntity::getDeviceId));
+                infusionByDeviceId.forEach((deviceId,infusions)->{
+                    String alias = deviceRegistry.getOperator(deviceId).getAlias();
+                    infusions.forEach(infusion->infusion.setAlias(alias));
+                });
+
+                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));
+                //标记输注
+                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));
+            }
         }
+
         return R.success(result);
     }
 

+ 1 - 1
coffee-system/src/main/java/com/coffee/bus/controller/BusDeviceHistoryController.java

@@ -57,7 +57,7 @@ public class BusDeviceHistoryController extends BaseCrudController<BusDeviceHist
     @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)){
+        if(!StrUtil.isNullOrUndefined(tenantId)){
             query.setTenantId(tenantId);
         }
         return R.success(deviceAlarmService.selectCountAlarm(query));

+ 55 - 10
coffee-system/src/main/java/com/coffee/bus/controller/BusStatsAnalyseController.java

@@ -1,11 +1,22 @@
 package com.coffee.bus.controller;
 
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import com.coffee.bus.enums.StatsAnalyseEnum;
+import com.coffee.bus.service.LocalBusInfusionHistoryService;
+import com.coffee.bus.service.dto.CombineQuery;
+import com.coffee.bus.service.dto.CombineResult;
+import com.coffee.bus.stats.CommonStats;
+import com.coffee.bus.stats.entity.StatsAnalyseResult;
+import com.coffee.common.exception.CustomException;
+import com.coffee.common.exception.TenantException;
+import com.coffee.common.result.R;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
-import lombok.AllArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import io.swagger.annotations.ApiParam;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
 
 /**
  * @author lifang
@@ -15,13 +26,47 @@ import org.springframework.web.bind.annotation.RestController;
  * @createTime 2022年04月08日 10:24:00
  */
 @RestController
-@AllArgsConstructor
-@RequestMapping("/bus/stats/analgesia")
-@Api(tags = "镇痛分析",description = "统一权限前缀(bus:analyse),例如新增bus:analyse:add")
+@RequestMapping("/stats/analgesia")
+@Api(tags = "镇痛分析",description = "统一权限前缀(stats:analyse),例如新增stats:analyse:add")
 public class BusStatsAnalyseController {
-    @GetMapping
-    @ApiOperation("改接口无意义,当该模块下存在接口时,将该接口删除")
-    public void show(){
+    private final Map<StatsAnalyseEnum,CommonStats> statsMap=new HashMap<>();
+
+    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();
+
 
     }
 }

+ 2 - 6
coffee-system/src/main/java/com/coffee/bus/controller/BusStatsReportController.java

@@ -16,12 +16,8 @@ import org.springframework.web.bind.annotation.RestController;
  */
 @RestController
 @AllArgsConstructor
-@RequestMapping("/bus/stats/report")
-@Api(tags = "报表分析",description = "统一权限前缀(bus:report),例如新增bus:report:add")
+@RequestMapping("/stats/report")
+@Api(tags = "报表分析",description = "统一权限前缀(stats:report),例如新增stats:report:add")
 public class BusStatsReportController {
-    @GetMapping
-    @ApiOperation("改接口无意义,当该模块下存在接口时,将该接口删除")
-    public void show(){
 
-    }
 }

+ 1 - 0
coffee-system/src/main/java/com/coffee/bus/entity/BusClinicEntity.java

@@ -118,4 +118,5 @@ public class BusClinicEntity extends TenantGenericEntity<String,String> {
 
     @ApiModelProperty(value = "医嘱")
     private String entrust;
+
 }

+ 4 - 4
coffee-system/src/main/java/com/coffee/bus/enums/DeviceTypeEnum.java

@@ -17,10 +17,10 @@ import lombok.Getter;
 @JsonFormat(shape = JsonFormat.Shape.OBJECT)
 public enum DeviceTypeEnum implements IEnum<Integer> {
     no(0,"未知"),
-    continuous(1,"网络泵"),
-    intelligent(2,"智能"),
-    pulse(3,"脉冲"),
-    other(4,"其他");
+    continuous(1,"持续型"),
+    intelligent(2,"智能"),
+    pulse(3,"脉冲"),
+    other(4,"其他");
 
     @Getter
     @ApiModelProperty("设备编码")

+ 48 - 0
coffee-system/src/main/java/com/coffee/bus/enums/StatsAnalyseEnum.java

@@ -0,0 +1,48 @@
+package com.coffee.bus.enums;
+
+import com.baomidou.mybatisplus.annotation.IEnum;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName StatsAnalyseEnum.java
+ * @Description 镇痛分析枚举
+ * @createTime 2022年06月06日 10:17:00
+ */
+@AllArgsConstructor
+@Getter
+public enum  StatsAnalyseEnum implements IEnum<Integer> {
+    anal(1,"pca镇痛分析"),
+    alarm(2,"提示分析"),
+    eval(3,"评价分析"),
+    pca(4,"pca自控分析"),
+    infusion(5,"输注量分析"),
+
+    gender(6,"性别分析"),
+    age(7,"年龄分析"),
+    weight(8,"体重分析"),
+    asa(9,"asa分析"),
+    genderAndAge(10,"性别年龄分析")
+    ;
+    private Integer value;
+    @Getter
+    private String text;
+
+    public static StatsAnalyseEnum of(Integer value){
+        switch (value){
+            case 1:
+                return anal;
+            case 2:
+                return alarm;
+            case 3:
+                return eval;
+            case 4:
+                return pca;
+            case 5:
+                return infusion;
+            default: return null;
+        }
+    }
+}

+ 50 - 0
coffee-system/src/main/java/com/coffee/bus/enums/StatsTimeUnit.java

@@ -0,0 +1,50 @@
+package com.coffee.bus.enums;
+
+import cn.hutool.core.date.DateUtil;
+import com.baomidou.mybatisplus.annotation.IEnum;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.Date;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName StatsTimeUnit.java
+ * @Description 统计日期枚举类
+ * @createTime 2022年06月06日 11:46:00
+ */
+@Getter
+@AllArgsConstructor
+public enum  StatsTimeUnit implements IEnum<Integer> {
+    DAY(1){
+        @Override
+        public String parse(Date date) {
+            return DateUtil.format(date,"yyyy年MM月dd日");
+        }
+    },
+    WEEK(2){
+        @Override
+        public String parse(Date date) {
+            String prefix= DateUtil.format(date, "yyyy年MM月");
+            return prefix+"第"+DateUtil.weekOfMonth(date)+"周";
+        }
+    },
+    MONTH(3){
+        @Override
+        public String parse(Date date) {
+            return DateUtil.format(date, "yyyy年MM月");
+        }
+    };
+    private Integer value;
+
+
+    /**
+     * 描述: 将时间戳转为相应的时间字符串传给前端
+     * @author lifang
+     * @date 2022/6/6 12:00
+     * @param date
+     * @return String
+     */
+    public abstract String parse(Date date);
+}

+ 21 - 1
coffee-system/src/main/java/com/coffee/bus/mapper/BusInfusionHistoryMapper.java

@@ -4,11 +4,12 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.coffee.bus.entity.BusInfusionHistoryEntity;
+import com.coffee.bus.service.dto.CombineAlarmResult;
 import com.coffee.bus.service.dto.CombineQuery;
 import com.coffee.bus.service.dto.CombineResult;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
-import org.apache.ibatis.annotations.Select;
+import java.util.*;
 
 /**
  * @author 龙三郎
@@ -38,4 +39,23 @@ public interface BusInfusionHistoryMapper extends BaseMapper<BusInfusionHistoryE
      * @return IPage<CombineResult>
      */
     IPage<CombineResult> queryPage(@Param("query") CombineQuery query, Page<CombineResult> page);
+
+
+    /**
+     * 描述: 联合查询出输注报警信息-统计使用
+     * @author lifang
+     * @date 2022/6/7 10:03
+     * @param query
+     * @return List<CombineAlarmResult>
+     */
+    List<CombineAlarmResult> queryStatsAlarm(@Param("query") CombineQuery query);
+
+    /**
+     * 描述: 联合查询出输注报警信息-统计使用
+     * @author lifang
+     * @date 2022/6/7 10:03
+     * @param query
+     * @return List<CombineAlarmResult>
+     */
+    List<CombineResult> queryStatsAnal(@Param("query") CombineQuery query);
 }

+ 10 - 5
coffee-system/src/main/java/com/coffee/bus/service/LocalBusInfusionHistoryService.java

@@ -18,10 +18,7 @@ import com.coffee.bus.listener.event.infusion.InfusionUpdateEvent;
 import com.coffee.bus.mapper.BusInfusionHistoryMapper;
 import com.coffee.bus.registry.device.DeviceOperator;
 import com.coffee.bus.registry.device.DeviceRegistry;
-import com.coffee.bus.service.dto.CombineQuery;
-import com.coffee.bus.service.dto.CombineResult;
-import com.coffee.bus.service.dto.ManualUndoConfig;
-import com.coffee.bus.service.dto.UndoDeviceConfig;
+import com.coffee.bus.service.dto.*;
 import com.coffee.common.crud.BaseService;
 import com.coffee.common.exception.CustomException;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -238,7 +235,7 @@ public class LocalBusInfusionHistoryService extends BaseService<BusInfusionHisto
                 BusPatientEntity patient = patientService.getOne(new QueryWrapper<BusPatientEntity>().lambda()
                         .eq(BusPatientEntity::getCode, manualUndoConfig.getPatientCode())
                         .eq(StrUtil.isNotEmpty(manualUndoConfig.getTenantId()), BusPatientEntity::getTenantId, manualUndoConfig.getTenantId()));
-                //去除主id
+                //去除主输注id
                 infusionIds=infusionIds.stream().filter(id->!id.equals(patient.getInfusionId())).collect(Collectors.toList());
                 //输注结束,更新撤泵信息
                 if(CollectionUtil.isNotEmpty(infusionIds)){
@@ -365,4 +362,12 @@ public class LocalBusInfusionHistoryService extends BaseService<BusInfusionHisto
     public IPage<CombineResult> queryPage(CombineQuery query, Page<CombineResult> page) {
         return this.baseMapper.queryPage(query,page);
     }
+
+    public List<CombineResult> queryStatsAnal(CombineQuery query) {
+        return this.baseMapper.queryStatsAnal(query);
+    }
+
+    public List<CombineAlarmResult> queryStatsAlarm(CombineQuery query) {
+        return this.baseMapper.queryStatsAlarm(query);
+    }
 }

+ 2 - 0
coffee-system/src/main/java/com/coffee/bus/service/dto/ClinicAnalClinicRecord.java

@@ -74,6 +74,8 @@ public class ClinicAnalClinicRecord  implements Serializable {
     @Length(max = 255,message = "手术医生不得超过255个字节")
     private String surgeryDoctor;
 
+    @ApiModelProperty(value = "医院名称")
+    private String tenantName;
 
     public static ClinicAnalClinicRecord parse(BusClinicEntity clinic){
         ClinicAnalClinicRecord clinicRecord = new ClinicAnalClinicRecord();

+ 24 - 0
coffee-system/src/main/java/com/coffee/bus/service/dto/CombineAlarmResult.java

@@ -0,0 +1,24 @@
+package com.coffee.bus.service.dto;
+
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName CombineAlarmResult.java
+ * @Description TODO
+ * @createTime 2022年06月07日 09:50:00
+ */
+@Data
+public class CombineAlarmResult extends CombineResult {
+
+    @ApiModelProperty(value = "报警原因")
+    private String cause;
+
+    @ApiModelProperty(value = "是否为报警信息",hidden = true)
+    @JsonIgnoreProperties
+    private Boolean isAlarm;
+}

+ 3 - 0
coffee-system/src/main/java/com/coffee/bus/service/dto/CombineQuery.java

@@ -2,6 +2,7 @@ package com.coffee.bus.service.dto;
 
 import cn.hutool.core.collection.CollectionUtil;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.coffee.bus.enums.StatsTimeUnit;
 import com.coffee.bus.enums.WarnEnum;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import io.swagger.annotations.ApiModelProperty;
@@ -128,6 +129,8 @@ public class CombineQuery {
     @NotNull(message = "分页参数不可为空")
     private Page<CombineResult> page;
 
+    @ApiModelProperty("统计查询时的时间枚举条件, 1、天 2、周 3、月")
+    private StatsTimeUnit timeUnit;
     public void setPcaCountRange(List<Integer> pcaCountRange) {
         this.pcaCountRange = pcaCountRange;
         parsePca();

+ 5 - 0
coffee-system/src/main/java/com/coffee/bus/service/dto/CombineResult.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler;
 import com.coffee.bus.entity.common.CommonDeviceParam;
 import com.coffee.common.enums.SexEnum;
+import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -100,4 +101,8 @@ public class CombineResult extends CommonDeviceParam<String,String> {
 
     @ApiModelProperty(value = "医嘱")
     private String entrust;
+
+    @ApiModelProperty(value = "时间根据 年、月、日解析结果",hidden = true)
+    @JsonIgnore
+    private String timeParseResult;
 }

+ 104 - 0
coffee-system/src/main/java/com/coffee/bus/stats/CommonStats.java

@@ -0,0 +1,104 @@
+package com.coffee.bus.stats;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.coffee.bus.enums.StatsAnalyseEnum;
+import com.coffee.bus.enums.StatsTimeUnit;
+import com.coffee.bus.service.dto.CombineQuery;
+import com.coffee.bus.service.dto.CombineResult;
+import com.coffee.bus.stats.entity.LineResult;
+import com.coffee.bus.stats.entity.PieResult;
+import com.coffee.bus.stats.entity.StatsColumn;
+import com.coffee.bus.stats.entity.TableResult;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName CommonStats.java
+ * @Description 统计
+ * @createTime 2022年06月06日 09:11:00
+ */
+public interface CommonStats<T extends CombineResult> {
+
+    List<T> queryResult(CombineQuery query);
+    /**
+     * 描述: 统计名称
+     * @author lifang
+     * @date 2022/6/6 9:12
+     * @param
+     * @return String
+     */
+    StatsAnalyseEnum getId();
+
+    /**
+     * 描述: 处理饼图
+     * @author lifang
+     * @date 2022/6/6 10:15
+     * @param results
+     * @return PieResult
+     */
+    List<PieResult> handlePie(List<T> results);
+
+    /**
+     * 描述: 处理折线图
+     * @author lifang
+     * @date 2022/6/6 10:15
+     * @param results
+     * @return LineResult
+     */
+    List<LineResult> handleLine(List<T> results, StatsTimeUnit unit);
+
+    /**
+     * 描述: 处理图表
+     * @author lifang
+     * @date 2022/6/6 10:16
+     * @param results
+     * @return TableResult
+     */
+    TableResult handleTable(List<T> results, StatsTimeUnit unit);
+
+
+
+    default Map<String, List<T>> groupByTime(List<T> results, StatsTimeUnit unit){
+        Map<String, List<T>> result = new LinkedHashMap<>();
+        results.forEach(combineResult->{
+            combineResult.setTimeParseResult(unit.parse(combineResult.getInfusionStartTime()));
+            String timeFormat = unit.parse(combineResult.getInfusionStartTime());
+            result.computeIfAbsent(timeFormat,k->new ArrayList<>()).add(combineResult);
+        });
+        return result;
+    }
+
+    default List<StatsColumn> getAllColumn(List<String> allAnalName){
+        List<StatsColumn> columnNames = new ArrayList<>();
+        columnNames.add(StatsColumn.of("时间","time"));
+        for (String name : allAnalName) {
+            columnNames.add(StatsColumn.of(name,name));
+            columnNames.add(StatsColumn.of(name,name,true));
+        }
+        return columnNames;
+    }
+
+    default BigDecimal computeRatio(BigDecimal count,BigDecimal total){
+        if(total.intValue()==0){
+            return BigDecimal.valueOf(0);
+        }
+        return count
+                .divide(total,2, RoundingMode.HALF_UP)
+                .multiply(BigDecimal.valueOf(100));
+    }
+
+    default  BigDecimal computeStandardDeviation(List<BigDecimal> sources,BigDecimal average){
+        BigDecimal subTotal=BigDecimal.valueOf(0);
+        if(CollectionUtil.size(sources)<2){
+            return subTotal;
+        }
+        for (BigDecimal source : sources) {
+            subTotal=subTotal.add(source.subtract(average).pow(2));
+        }
+
+        return  BigDecimal.valueOf(Math.sqrt(subTotal.divide(BigDecimal.valueOf(CollectionUtil.size(sources)),2,RoundingMode.HALF_UP).doubleValue())).abs().setScale(2,BigDecimal.ROUND_HALF_UP);
+    }
+}

+ 281 - 0
coffee-system/src/main/java/com/coffee/bus/stats/analyse/AlarmStatsAnalyse.java

@@ -0,0 +1,281 @@
+package com.coffee.bus.stats.analyse;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import com.coffee.bus.entity.BusConMixEntity;
+import com.coffee.bus.enums.DeviceAlarmEnum;
+import com.coffee.bus.enums.StatsAnalyseEnum;
+import com.coffee.bus.enums.StatsTimeUnit;
+import com.coffee.bus.service.LocalBusInfusionHistoryService;
+import com.coffee.bus.service.dto.CombineAlarmResult;
+import com.coffee.bus.service.dto.CombineQuery;
+import com.coffee.bus.stats.CommonStats;
+import com.coffee.bus.stats.entity.*;
+import com.coffee.bus.stats.enums.PieEnum;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AlarmAndWarnStatsAnalyse.java
+ * @Description 提示统计
+ * @createTime 2022年06月07日 10:04:00
+ */
+@Data
+@AllArgsConstructor
+@Service
+public class AlarmStatsAnalyse implements CommonStats<CombineAlarmResult> {
+    private final LocalBusInfusionHistoryService infusionHistoryService;
+    @Override
+    public List<CombineAlarmResult> queryResult(CombineQuery query) {
+        return infusionHistoryService.queryStatsAlarm(query);
+    }
+
+    @Override
+    public StatsAnalyseEnum getId() {
+        return StatsAnalyseEnum.alarm;
+    }
+
+    @Override
+    public List<PieResult> handlePie(List<CombineAlarmResult> results) {
+        ArrayList<PieResult> result = new ArrayList<>();
+        result.add(totalPie(results));
+        result.addAll(alarmAnalysePie(results));
+        return result;
+    }
+
+    @Override
+    public List<LineResult> handleLine(List<CombineAlarmResult> results, StatsTimeUnit unit) {
+        //根据时间对结果进行区分统计
+        Map<String, List<CombineAlarmResult>> groupByTime = groupByTime(results, unit);
+        List<LineResult> result = new ArrayList<>();
+        result.add(countLine(groupByTime));
+        result.add(ratioLine(groupByTime));
+        //比率
+        return result;
+    }
+
+    /**
+     * 描述: 提示比率折线图
+     * @author lifang
+     * @date 2022/6/7 13:24
+     * @param results
+     * @return LineResult
+     */
+    private LineResult ratioLine(Map<String, List<CombineAlarmResult>> results) {
+        LineResult<BigDecimal> result = new LineResult<>();
+        List<String> time = new ArrayList<>();
+        result.setTime(time);
+        result.setId("count");
+        result.setDescription("此数据为 报警率 【次数统计】");
+        Map<String, AlarmTotalPieResult> groupByTime = new HashMap<>();
+        //根据时间区间对镇痛方式进行统计
+        results.forEach((timeRange,combineResults)->{
+            AlarmTotalPieResult alarmTotalPieResult = groupByTime.computeIfAbsent(timeRange, k -> new AlarmTotalPieResult());
+            combineResults.forEach(alarmTotalPieResult::handle);
+            time.add(timeRange);
+        });
+        //将每一时区的报警数量进行分类汇总
+        Map<String, LineResult.LineContent<BigDecimal>> lineContentMap=new HashMap<>();
+        groupByTime.forEach((timeRange,totalResult)->{
+            BigDecimal total = BigDecimal.valueOf(totalResult.getTotal());
+            lineContentMap.computeIfAbsent("堵塞率",k->new LineResult.LineContent<>("堵塞")).getValue()
+                    .add(computeRatio(BigDecimal.valueOf(totalResult.getJam().getValue()),total));
+            lineContentMap.computeIfAbsent("极限率",k->new LineResult.LineContent<>("极限")).getValue()
+                    .add(computeRatio(BigDecimal.valueOf(totalResult.getLimit().getValue()),total));
+            lineContentMap.computeIfAbsent("未装药盒率",k->new LineResult.LineContent<>("未装药盒")).getValue()
+                    .add(computeRatio(BigDecimal.valueOf(totalResult.getNoBox().getValue()),total));
+            lineContentMap.computeIfAbsent("电机失控率",k->new LineResult.LineContent<>("电机失控")).getValue()
+                    .add(computeRatio(BigDecimal.valueOf(totalResult.getOutOfControl().getValue()),total));
+            lineContentMap.computeIfAbsent("镇痛不足率",k->new LineResult.LineContent<>("镇痛不足")).getValue()
+                    .add(computeRatio(BigDecimal.valueOf(totalResult.getAnalgesicPoor().getValue()),total));
+            lineContentMap.computeIfAbsent("不在服务区率",k->new LineResult.LineContent<>("不在服务区")).getValue()
+                    .add(computeRatio(BigDecimal.valueOf(totalResult.getNoSignal().getValue()),total));
+            lineContentMap.computeIfAbsent("机械故障率",k->new LineResult.LineContent<>("机械故障")).getValue()
+                    .add(computeRatio(BigDecimal.valueOf(totalResult.getMachine().getValue()),total));
+            lineContentMap.computeIfAbsent("电量耗尽率",k->new LineResult.LineContent<>("电量耗尽")).getValue()
+                    .add(computeRatio(BigDecimal.valueOf(totalResult.getLowBattery().getValue()),total));
+            lineContentMap.computeIfAbsent("气泡无液率",k->new LineResult.LineContent<>("气泡无液")).getValue()
+                    .add(computeRatio(BigDecimal.valueOf(totalResult.getBubble().getValue()),total));
+            lineContentMap.computeIfAbsent("低输注率",k->new LineResult.LineContent<>("低输注")).getValue()
+                    .add(computeRatio(BigDecimal.valueOf(totalResult.getLowInfusion().getValue()),total));
+        });
+        result.setContent(new ArrayList<>(lineContentMap.values()));
+        return result;
+    }
+
+    /**
+     * 描述: 提示次数折线图
+     * @author lifang
+     * @date 2022/6/7 13:24
+     * @param results
+     * @return LineResult
+     */
+    private LineResult countLine(Map<String, List<CombineAlarmResult>> results) {
+        LineResult<Long> result = new LineResult<>();
+        List<String> time = new ArrayList<>();
+        result.setTime(time);
+        result.setId("count");
+        result.setDescription("此数据为 报警方式 【次数统计】");
+        Map<String, AlarmTotalPieResult> groupByTime = new HashMap<>();
+        //根据时间区间对镇痛方式进行统计
+        results.forEach((timeRange,combineResults)->{
+            AlarmTotalPieResult alarmTotalPieResult = groupByTime.computeIfAbsent(timeRange, k -> new AlarmTotalPieResult());
+            combineResults.forEach(alarmTotalPieResult::handle);
+            time.add(timeRange);
+        });
+        //将每一时区的报警数量进行分类汇总
+        Map<String, LineResult.LineContent<Long>> lineContentMap=new HashMap<>();
+        groupByTime.forEach((timeRange,totalResult)->{
+            lineContentMap.computeIfAbsent("堵塞",k->new LineResult.LineContent<>("堵塞")).getValue().add(totalResult.getJam().getValue());
+            lineContentMap.computeIfAbsent("极限",k->new LineResult.LineContent<>("极限")).getValue().add(totalResult.getLimit().getValue());
+            lineContentMap.computeIfAbsent("未装药盒",k->new LineResult.LineContent<>("未装药盒")).getValue().add(totalResult.getNoBox().getValue());
+            lineContentMap.computeIfAbsent("电机失控",k->new LineResult.LineContent<>("电机失控")).getValue().add(totalResult.getOutOfControl().getValue());
+            lineContentMap.computeIfAbsent("镇痛不足",k->new LineResult.LineContent<>("镇痛不足")).getValue().add(totalResult.getAnalgesicPoor().getValue());
+            lineContentMap.computeIfAbsent("不在服务区",k->new LineResult.LineContent<>("不在服务区")).getValue().add(totalResult.getNoSignal().getValue());
+            lineContentMap.computeIfAbsent("机械故障",k->new LineResult.LineContent<>("机械故障")).getValue().add(totalResult.getMachine().getValue());
+            lineContentMap.computeIfAbsent("电量耗尽",k->new LineResult.LineContent<>("电量耗尽")).getValue().add(totalResult.getLowBattery().getValue());
+            lineContentMap.computeIfAbsent("气泡无液",k->new LineResult.LineContent<>("气泡无液")).getValue().add(totalResult.getBubble().getValue());
+            lineContentMap.computeIfAbsent("低输注",k->new LineResult.LineContent<>("低输注")).getValue().add(totalResult.getLowInfusion().getValue());
+        });
+        result.setContent(new ArrayList<>(lineContentMap.values()));
+        return result;
+    }
+
+
+
+    @Override
+    public TableResult handleTable(List<CombineAlarmResult> results, StatsTimeUnit unit) {
+        TableResult result = new TableResult();
+        //根据时间对结果进行区分统计
+        Map<String, List<CombineAlarmResult>> groupByTimeResults = groupByTime(results, unit);
+        //顺序不可错! 先数量后比率
+        List<StatsColumn> allColumn = getAllColumn(Arrays.asList("堵塞","极限","未装药盒","电机失控","镇痛不足","不在服务区","机械故障","电量耗尽","气泡无液","返厂维护","低输注"));
+        allColumn.add(1,StatsColumn.of("输注总数","输注总数"));
+        allColumn.add(2,StatsColumn.of("提示总数","提示总数"));
+        List<Map<String, Object>> contents = new ArrayList<>();
+        result.setContent(contents);
+        result.setColumn(allColumn);
+        //根据时间区间对镇痛方式进行统计
+        Map<String, AlarmTotalPieResult> groupByTimeCountResult = new HashMap<>();
+        //根据时间区间对镇痛方式进行统计
+        groupByTimeResults.forEach((timeRange,combineResults)->{
+            AlarmTotalPieResult alarmTotalPieResult = groupByTimeCountResult.computeIfAbsent(timeRange, k -> new AlarmTotalPieResult());
+            combineResults.forEach(alarmTotalPieResult::handle);
+            alarmTotalPieResult.setInfusionCount(combineResults.stream().map(CombineAlarmResult::getId).distinct().count());
+        });
+        groupByTimeCountResult.forEach((timeRange,totalResult)->{
+            //表格内容
+            LinkedHashMap<String, Object> contentValues = new LinkedHashMap<>();
+            BigDecimal total =BigDecimal.valueOf( totalResult.getTotal());
+            contentValues.put("time",timeRange);
+            contentValues.put("输注总数",totalResult.getInfusionCount());
+            contentValues.put("提示总数",total);
+            contentValues.put("堵塞",totalResult.getJam().getValue());
+            contentValues.put("极限",totalResult.getLimit().getValue());
+            contentValues.put("未装药盒",totalResult.getNoBox().getValue());
+            contentValues.put("电机失控",totalResult.getOutOfControl().getValue());
+            contentValues.put("镇痛不足",totalResult.getAnalgesicPoor().getValue());
+            contentValues.put("不在服务区",totalResult.getNoSignal().getValue());
+            contentValues.put("机械故障",totalResult.getMachine().getValue());
+            contentValues.put("电量耗尽",totalResult.getLowBattery().getValue());
+            contentValues.put("气泡无液",totalResult.getBubble().getValue());
+            contentValues.put("低输注",totalResult.getLowInfusion().getValue());
+
+            contentValues.put("堵塞比率",computeRatio(BigDecimal.valueOf(totalResult.getJam().getValue()),total));
+            contentValues.put("极限比率",computeRatio(BigDecimal.valueOf(totalResult.getLimit().getValue()),total));
+            contentValues.put("未装药盒比率",computeRatio(BigDecimal.valueOf(totalResult.getNoBox().getValue()),total));
+            contentValues.put("电机失控比率",computeRatio(BigDecimal.valueOf(totalResult.getOutOfControl().getValue()),total));
+            contentValues.put("镇痛不足比率",computeRatio(BigDecimal.valueOf(totalResult.getAnalgesicPoor().getValue()),total));
+            contentValues.put("不在服务区比率",computeRatio(BigDecimal.valueOf(totalResult.getNoSignal().getValue()),total));
+            contentValues.put("机械故障比率",computeRatio(BigDecimal.valueOf(totalResult.getMachine().getValue()),total));
+            contentValues.put("电量耗尽比率",computeRatio(BigDecimal.valueOf(totalResult.getLowBattery().getValue()),total));
+            contentValues.put("气泡无液比率",computeRatio(BigDecimal.valueOf(totalResult.getBubble().getValue()),total));
+            contentValues.put("低输注比率",computeRatio(BigDecimal.valueOf(totalResult.getLowInfusion().getValue()),total));
+
+            contents.add(contentValues);
+        });
+        return result;
+    }
+
+
+    /**
+     * 描述: 获取总量饼图
+     * 1、堵塞
+     * 2、极限
+     * 3、未装药盒
+     * 4、电机失控
+     * 5、镇痛不足
+     * 6、不在服务区
+     * 7、机械故障
+     * 8、电量耗尽
+     * 9、气泡无液
+     * 10、低输注状态
+     * 11、返厂维护
+     * @author lifang
+     * @date 2022/6/7 10:07
+     * @param results
+     * @return PieResult
+     */
+    private PieResult totalPie(List<CombineAlarmResult> results){
+        PieResult result = PieResult.of(PieEnum.total.name(),PieEnum.total.getText());
+        AlarmTotalPieResult alarmTotalPieResult = new AlarmTotalPieResult();
+        results.forEach(alarmTotalPieResult::handle);
+        result.setContent(new ArrayList<>(alarmTotalPieResult.values()));
+        return result;
+    }
+
+    /**
+     * 描述: 报警原因分析饼图
+     * 堵塞
+     * 极限
+     * 未装药盒
+     * 电机失控
+     * 机械故障
+     * 电量耗尽
+     * 气泡无液
+     * 返厂维修
+     * @author lifang
+     * @date 2022/6/7 10:50
+     * @param results
+     * @return List<PieResult>
+     */
+    private List<PieResult> alarmAnalysePie(List<CombineAlarmResult> results){
+        Map<DeviceAlarmEnum, PieResult> resultMap = new HashMap<>();
+        resultMap.put(DeviceAlarmEnum.Jam,PieResult.of("jam","堵塞报警原因分析饼图"));
+        resultMap.put(DeviceAlarmEnum.Limit,PieResult.of("limit","极限报警原因分析饼图"));
+        resultMap.put(DeviceAlarmEnum.NotBox,PieResult.of("noBox","未装药盒报警原因分析饼图"));
+        resultMap.put(DeviceAlarmEnum.OutOfControl,PieResult.of("outOfControl","电机失控报警原因分析饼图"));
+        resultMap.put(DeviceAlarmEnum.Machine,PieResult.of("machine","机械故障报警原因分析饼图"));
+        resultMap.put(DeviceAlarmEnum.LowBattery,PieResult.of("lowBattery","电量耗尽报警原因分析饼图"));
+        resultMap.put(DeviceAlarmEnum.Bubble,PieResult.of("bubble","气泡无液报警原因分析饼图"));
+        resultMap.put(DeviceAlarmEnum.InfusionMax,PieResult.of("infusionMax","返厂维护报警原因分析饼图"));
+        //按照报警类型分类
+        Map<DeviceAlarmEnum, List<CombineAlarmResult>> alarmMap = results.stream()
+                .peek(r->{
+                    if(StrUtil.isBlank(r.getCause())){
+                        r.setCause("未填写");
+                    }
+                })
+                .filter(r -> Boolean.TRUE.equals(r.getIsAlarm())).collect(Collectors.groupingBy(CombineAlarmResult::getAlarm));
+
+        resultMap.forEach((alarmType,result)->{
+            List<CombineAlarmResult> alarmResults = alarmMap.get(alarmType);
+            if (CollectionUtil.isNotEmpty(alarmResults)) {
+                Map<String, List<CombineAlarmResult>> causeMap = alarmResults.stream().collect(Collectors.groupingBy(CombineAlarmResult::getCause));
+                causeMap.forEach((cause,alarmCauses)->{
+                    result.addContent(cause,Long.valueOf(CollectionUtil.size(alarmCauses)));
+                });
+            }else {
+                result.addContent("未填写",0);
+            }
+        });
+        return new ArrayList<>(resultMap.values());
+    }
+}

+ 251 - 0
coffee-system/src/main/java/com/coffee/bus/stats/analyse/AnalStatsAnalyse.java

@@ -0,0 +1,251 @@
+package com.coffee.bus.stats.analyse;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.coffee.bus.entity.BusConMixEntity;
+import com.coffee.bus.enums.ConstantMixEnum;
+import com.coffee.bus.enums.StatsAnalyseEnum;
+import com.coffee.bus.enums.StatsTimeUnit;
+import com.coffee.bus.service.LocalBusInfusionHistoryService;
+import com.coffee.bus.service.constant.LocalBusConMixService;
+import com.coffee.bus.service.dto.CombineQuery;
+import com.coffee.bus.service.dto.CombineResult;
+import com.coffee.bus.stats.CommonStats;
+import com.coffee.bus.stats.entity.LineResult;
+import com.coffee.bus.stats.entity.PieResult;
+import com.coffee.bus.stats.entity.StatsColumn;
+import com.coffee.bus.stats.entity.TableResult;
+import com.coffee.bus.stats.enums.PieEnum;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName PCAStatsAnalyse.java
+ * @Description 镇痛分析-pca分析
+ * @createTime 2022年06月06日 10:17:00
+ */
+@Component
+@AllArgsConstructor
+public class AnalStatsAnalyse implements CommonStats<CombineResult> {
+    private final LocalBusConMixService conMixService;
+    private final LocalBusInfusionHistoryService infusionHistoryService;
+    @Override
+    public List<CombineResult> queryResult(CombineQuery query) {
+        return infusionHistoryService.queryStatsAnal(query);
+    }
+
+    @Override
+    public StatsAnalyseEnum getId() {
+        return StatsAnalyseEnum.anal;
+    }
+
+    @Override
+    public List<PieResult> handlePie(List<CombineResult> results) {
+        PieResult result = PieResult.of(PieEnum.total.name(),PieEnum.total.getText());
+        //keys由历史数据和常量维护信息表并集构成
+        Map<String, Long> analMaps = groupByAnal(results);
+        List<String> allAnal = getAllAnalName(analMaps, getConAnal());
+        allAnal.forEach(anal->{
+            Long analCount = analMaps.get(anal);
+            if(analCount==null||analCount==0){
+                result.addContent(anal,0L);
+            }else {
+                result.addContent(anal,analCount);
+            }
+        });
+        return Arrays.asList(result);
+    }
+
+    @Override
+    public List<LineResult> handleLine(List<CombineResult> results, StatsTimeUnit unit) {
+        //根据时间对结果进行区分统计
+        Map<String, List<CombineResult>> groupByTime = groupByTime(results, unit);
+        //获取所有镇痛信息
+        List<String> allAnal = getAllAnalName( groupByAnal(results), getConAnal());
+        List<LineResult> result = new ArrayList<>();
+        result.add(countLine(groupByTime,allAnal));
+        result.add(ratioLine(groupByTime,allAnal));
+        //比率
+        return result;
+    }
+
+
+    /**
+     * 描述: 表格处理
+     * @author lifang
+     * @date 2022/6/6 21:19
+     * @param results
+     * @param unit
+     * @return TableResult
+     */
+    @Override
+    public TableResult handleTable(List<CombineResult> results, StatsTimeUnit unit) {
+        TableResult result = new TableResult();
+        //根据时间对结果进行区分统计
+        Map<String, LineResult.LineContent<Long>> content = new HashMap<>();
+        Map<String, List<CombineResult>> groupByTime = groupByTime(results, unit);
+        List<String> allAnal = getAllAnalName( groupByAnal(results), getConAnal());
+        List<StatsColumn> allColumn = getAllColumn(allAnal);
+        allColumn.add(1,StatsColumn.of("输注总数","输注总数"));
+        List<Map<String, Object>> contents = new ArrayList<>();
+        result.setContent(contents);
+        result.setColumn(allColumn);
+        //根据时间区间对镇痛方式进行统计
+        groupByTime.forEach((timeRange,combineResult)->{
+            BigDecimal totalSize = BigDecimal.valueOf(CollectionUtil.size(combineResult));
+            //已有的镇痛方式数量
+            Map<String, Long> groupByAnal = groupByAnal(combineResult);
+            //表格内容
+            LinkedHashMap<String, Object> contentValues = new LinkedHashMap<>();
+            contentValues.put("time",timeRange);
+            contentValues.put("输注总数",combineResult.stream().map(CombineResult::getId).distinct().count());
+            //获取特定时间区间内 各镇痛方式的 数量
+            allAnal.forEach(anal->{
+                //折线图内容
+                LineResult.LineContent<Long> lineContent = content.computeIfAbsent(anal, analName -> new LineResult.LineContent<>(analName));
+                List<Long> values = Optional.ofNullable(lineContent.getValue()).orElse(new ArrayList<>());
+                Long analCount = groupByAnal.get(anal);
+                if(analCount==null||analCount==0){
+                    contentValues.put(anal,0);
+                }else {
+                    contentValues.put(anal,analCount);
+                }
+                if(analCount==null||analCount==0){
+                    contentValues.put(StatsColumn.ratioColumnName(anal),BigDecimal.valueOf(0L));
+                }else {
+                    contentValues.put(StatsColumn.ratioColumnName(anal),computeRatio(BigDecimal.valueOf(analCount),totalSize));
+                }
+                lineContent.setValue(values);
+            });
+            contents.add(contentValues);
+        });
+
+        return result;
+    }
+
+
+
+
+    private Map<String,Long> groupByAnal(List<CombineResult> results){
+        return  results.stream()
+                .peek(result -> {
+                    //将空的镇痛方式置为其他
+                    if (StrUtil.isNullOrUndefined(result.getAnalType())) {
+                        result.setAnalType("其他");
+                    }
+                })
+                .collect(Collectors.groupingBy(CombineResult::getAnalType,Collectors.counting()));
+    }
+
+    /**
+     * 描述: 获取当前医院下的所有镇痛方式常量
+     * @author lifang
+     * @date 2022/6/6 11:23
+     * @param
+     * @return List<BusConMixEntity>
+     */
+    private  List<BusConMixEntity> getConAnal(){
+        return  conMixService. list(new QueryWrapper<BusConMixEntity>().lambda().eq(BusConMixEntity::getType, ConstantMixEnum.anal));
+    }
+
+
+    private List<String> getAllAnalName(Map<String,?> analMaps,List<BusConMixEntity> conAnal){
+        List<String> keys = new LinkedList<>();
+        if (CollectionUtil.isNotEmpty(conAnal)&&CollectionUtil.isNotEmpty(analMaps)) {
+            //取得并集
+            keys.addAll(CollectionUtil.union(analMaps.keySet(),
+                    conAnal
+                            .stream()
+                            .map(BusConMixEntity::getName).collect(Collectors.toList())));
+        } else if(CollectionUtil.isNotEmpty(conAnal)){
+            keys=conAnal.stream().map(BusConMixEntity::getName).collect(Collectors.toList());
+        }else if(CollectionUtil.isNotEmpty(analMaps)){
+            keys.addAll(analMaps.keySet());
+        }
+        return keys;
+    }
+
+    /**
+     * 描述:镇痛方式次数折线图
+     * @author lifang
+     * @date 2022/6/7 13:24
+     * @param groupByTime
+     * @param allAnal
+     * @return LineResult<Long>
+     */
+    private LineResult<Long> countLine( Map<String, List<CombineResult>> groupByTime, List<String> allAnal){
+        LineResult<Long> result = new LineResult<>();
+        Map<String, LineResult.LineContent<Long>> content = new HashMap<>();
+        List<String> time = new ArrayList<>();
+        result.setTime(time);
+        result.setId("count");
+        result.setDescription("此数据为 镇痛方式 【次数统计】");
+
+        //根据时间区间对镇痛方式进行统计
+        groupByTime.forEach((timeRange,combineResult)->{
+            //已有的镇痛方式数量
+            Map<String, Long> groupByAnal = groupByAnal(combineResult);
+            //获取特定时间区间内 各镇痛方式的 数量
+            allAnal.forEach(anal->{
+                //折线图内容
+                LineResult.LineContent<Long> lineContent = content.computeIfAbsent(anal, analName -> new LineResult.LineContent<>(analName));
+                List<Long> values = Optional.ofNullable(lineContent.getValue()).orElse(new ArrayList<>());
+                Long analCount = groupByAnal.get(anal);
+                if(analCount==null||analCount==0){
+                    values.add(0L);
+                }else {
+                    values.add(analCount);
+                }
+                lineContent.setValue(values);
+            });
+            time.add(timeRange);
+        });
+        result.setContent(new ArrayList<>(content.values()));
+        return result;
+    }
+
+
+
+    private LineResult ratioLine( Map<String, List<CombineResult>> groupByTime, List<String> allAnal) {
+        LineResult<BigDecimal> result = new LineResult<>();
+        Map<String, LineResult.LineContent<BigDecimal>> content = new HashMap<>();
+        result.setContent(new ArrayList<>(content.values()));
+        List<String> time = new ArrayList<>();
+        result.setTime(time);
+        result.setId("ratio");
+        result.setDescription("此数据为 镇痛方式 【比率统计】");
+
+        //根据时间区间对镇痛方式进行统计
+        groupByTime.forEach((timeRange,combineResult)->{
+            //已有的镇痛方式数量
+            BigDecimal totalSize = BigDecimal.valueOf(CollectionUtil.size(combineResult));
+            Map<String, Long> groupByAnal = groupByAnal(combineResult);
+            //获取特定时间区间内 各镇痛方式的 数量
+            allAnal.forEach(anal->{
+                //折线图内容
+                LineResult.LineContent<BigDecimal> lineContent = content.computeIfAbsent(anal, analName -> new LineResult.LineContent<>(analName));
+                List<BigDecimal> values = Optional.ofNullable(lineContent.getValue()).orElse(new ArrayList<>());
+                Long analCount = groupByAnal.get(anal);
+                if(analCount==null||analCount==0){
+                    values.add(BigDecimal.valueOf(0L));
+                }else {
+                    values.add(computeRatio(BigDecimal.valueOf(analCount),totalSize));
+                    values.add(BigDecimal.valueOf(analCount).divide(totalSize, 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)));
+                }
+                lineContent.setValue(values);
+            });
+            time.add(timeRange);
+        });
+        result.setContent(new ArrayList<>(content.values()));
+        return result;
+    }
+
+}

+ 11 - 0
coffee-system/src/main/java/com/coffee/bus/stats/analyse/EvalStatsAnalyse.java

@@ -0,0 +1,11 @@
+package com.coffee.bus.stats.analyse;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName EvalStatsAnalyse.java
+ * @Description TODO
+ * @createTime 2022年06月07日 21:10:00
+ */
+public class EvalStatsAnalyse {
+}

+ 224 - 0
coffee-system/src/main/java/com/coffee/bus/stats/analyse/InfusionDoseStatsAnalyse.java

@@ -0,0 +1,224 @@
+package com.coffee.bus.stats.analyse;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.coffee.bus.enums.StatsAnalyseEnum;
+import com.coffee.bus.enums.StatsTimeUnit;
+import com.coffee.bus.service.LocalBusInfusionHistoryService;
+import com.coffee.bus.service.dto.CombineQuery;
+import com.coffee.bus.service.dto.CombineResult;
+import com.coffee.bus.stats.CommonStats;
+import com.coffee.bus.stats.entity.LineResult;
+import com.coffee.bus.stats.entity.PieResult;
+import com.coffee.bus.stats.entity.TableResult;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import javax.validation.constraints.DecimalMax;
+import javax.validation.constraints.DecimalMin;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName InfusionDoseStatsAnalyse.java
+ * @Description 输注量分析
+ * @createTime 2022年06月07日 21:12:00
+ */
+@AllArgsConstructor
+@Service
+public class InfusionDoseStatsAnalyse implements CommonStats<CombineResult> {
+    private final LocalBusInfusionHistoryService infusionHistoryService;
+    @Override
+    public List<CombineResult> queryResult(CombineQuery query) {
+        return infusionHistoryService.queryStatsAnal(query);
+    }
+
+    @Override
+    public StatsAnalyseEnum getId() {
+        return StatsAnalyseEnum.infusion;
+    }
+
+    @Override
+    public List<PieResult> handlePie(List<CombineResult> results) {
+        return null;
+    }
+
+    /**
+     * 描述: 输注量分析需要 最大值、最小值、均数、标准差
+     * @author lifang
+     * @date 2022/6/7 21:28
+     * @param results
+     * @param unit
+     * @return List<LineResult>
+     */
+    @Override
+    public List<LineResult> handleLine(List<CombineResult> results, StatsTimeUnit unit) {
+        //根据时间对结果进行区分统计
+        Map<String, List<CombineResult>> groupByTime = groupByTime(results, unit);
+        //获取所有镇痛信息
+        List<LineResult> result = new ArrayList<>();
+        result.add(inputDoseLine(groupByTime));
+        result.add(continueDoseLine(groupByTime));
+        result.add(appendDoseLine(groupByTime));
+        result.add(selfLockTimeLine(groupByTime));
+        //比率
+        return result;
+    }
+
+    private LineResult selfLockTimeLine(Map<String, List<CombineResult>> groupByTime) {
+        LineResult<BigDecimal> result = new LineResult<>();
+
+        List<String> time = new ArrayList<>();
+        result.setTime(time);
+        result.setId("selfLockTime");
+        result.setDescription("此数据为 自控锁时分析");
+
+        LineResult.LineContent<BigDecimal> maxLine = new LineResult.LineContent<>("最大值");
+        LineResult.LineContent<BigDecimal> minLine = new LineResult.LineContent<>("最小值");
+        LineResult.LineContent<BigDecimal> averageLine = new LineResult.LineContent<>("均值");
+        LineResult.LineContent<BigDecimal> standardDeviationLine = new LineResult.LineContent<>("均值±标准差");
+        //根据时间区间对镇痛方式进行统计
+        groupByTime.forEach((timeRange,combineResults)->{
+            List<BigDecimal> sources = combineResults.stream().peek(combineResult -> {
+                if (combineResult.getSelfControlLockTime() == null) {
+                    combineResult.setSelfControlLockTime(0);
+                }
+            })
+                    .map(CombineResult::getSelfControlLockTime)
+                    .map(BigDecimal::valueOf)
+                    .collect(Collectors.toList());
+            handle(sources,maxLine,minLine,averageLine,standardDeviationLine);
+            time.add(timeRange);
+        });
+        result.setContent(Arrays.asList(maxLine,minLine,averageLine,standardDeviationLine));
+        return result;
+    }
+
+    private LineResult appendDoseLine(Map<String, List<CombineResult>> groupByTime) {
+        LineResult<BigDecimal> result = new LineResult<>();
+
+        List<String> time = new ArrayList<>();
+        result.setTime(time);
+        result.setId("appendDoseLine");
+        result.setDescription("此数据为 追加量分析");
+
+        LineResult.LineContent<BigDecimal> maxLine = new LineResult.LineContent<>("最大值");
+        LineResult.LineContent<BigDecimal> minLine = new LineResult.LineContent<>("最小值");
+        LineResult.LineContent<BigDecimal> averageLine = new LineResult.LineContent<>("均值");
+        LineResult.LineContent<BigDecimal> standardDeviationLine = new LineResult.LineContent<>("均值±标准差");
+        //根据时间区间对镇痛方式进行统计
+        groupByTime.forEach((timeRange,combineResults)->{
+            List<BigDecimal> sources = combineResults.stream().peek(combineResult -> {
+                if (combineResult.getAppendDose() == null) {
+                    combineResult.setAppendDose(BigDecimal.valueOf(0));
+                }
+            })
+                    .map(CombineResult::getAppendDose)
+                    .collect(Collectors.toList());
+            handle(sources,maxLine,minLine,averageLine,standardDeviationLine);
+            time.add(timeRange);
+        });
+        result.setContent(Arrays.asList(maxLine,minLine,averageLine,standardDeviationLine));
+        return result;
+    }
+
+    /**
+     * 描述: 已输入量分析折线图 已输入量 最大值、最小值、均数、标准差
+     * @author lifang
+     * @date 2022/6/7 21:31
+     * @param groupByTime
+     * @return LineResult
+     */
+    private LineResult inputDoseLine(Map<String, List<CombineResult>> groupByTime) {
+        LineResult<BigDecimal> result = new LineResult<>();
+
+        List<String> time = new ArrayList<>();
+        result.setTime(time);
+        result.setId("inputDoseLine");
+        result.setDescription("此数据为 已输入量分析");
+
+        LineResult.LineContent<BigDecimal> maxLine = new LineResult.LineContent<>("最大值");
+        LineResult.LineContent<BigDecimal> minLine = new LineResult.LineContent<>("最小值");
+        LineResult.LineContent<BigDecimal> averageLine = new LineResult.LineContent<>("均值");
+        LineResult.LineContent<BigDecimal> standardDeviationLine = new LineResult.LineContent<>("均值±标准差");
+        //根据时间区间对镇痛方式进行统计
+        groupByTime.forEach((timeRange,combineResults)->{
+            List<BigDecimal> sources = combineResults.stream().peek(combineResult -> {
+                if (combineResult.getInputDose() == null) {
+                    combineResult.setInputDose(BigDecimal.valueOf(0));
+                }
+            })
+                    .map(CombineResult::getInputDose)
+                    .collect(Collectors.toList());
+            handle(sources,maxLine,minLine,averageLine,standardDeviationLine);
+            time.add(timeRange);
+        });
+        result.setContent(Arrays.asList(maxLine,minLine,averageLine,standardDeviationLine));
+        return result;
+    }
+
+    /**
+     * 描述: 已输入量分析折线图 已输入量 最大值、最小值、均数、标准差
+     * @author lifang
+     * @date 2022/6/7 21:31
+     * @param groupByTime
+     * @return LineResult
+     */
+    private LineResult continueDoseLine(Map<String, List<CombineResult>> groupByTime) {
+        LineResult<BigDecimal> result = new LineResult<>();
+
+        List<String> time = new ArrayList<>();
+        result.setTime(time);
+        result.setId("continueDoseLine");
+        result.setDescription("此数据为 持续量分析");
+
+        LineResult.LineContent<BigDecimal> maxLine = new LineResult.LineContent<>("最大值");
+        LineResult.LineContent<BigDecimal> minLine = new LineResult.LineContent<>("最小值");
+        LineResult.LineContent<BigDecimal> averageLine = new LineResult.LineContent<>("均值");
+        LineResult.LineContent<BigDecimal> standardDeviationLine = new LineResult.LineContent<>("均值±标准差");
+        //根据时间区间对镇痛方式进行统计
+        groupByTime.forEach((timeRange,combineResults)->{
+            List<BigDecimal> sources = combineResults.stream().peek(combineResult -> {
+                if (combineResult.getContinueDose() == null) {
+                    combineResult.setContinueDose(BigDecimal.valueOf(0));
+                }
+            })
+                    .map(CombineResult::getContinueDose)
+                    .collect(Collectors.toList());
+            handle(sources,maxLine,minLine,averageLine,standardDeviationLine);
+            time.add(timeRange);
+        });
+        result.setContent(Arrays.asList(maxLine,minLine,averageLine,standardDeviationLine));
+        return result;
+    }
+
+
+    private void handle(List<BigDecimal> sources,LineResult.LineContent<BigDecimal> maxLine, LineResult.LineContent<BigDecimal> minLine, LineResult.LineContent<BigDecimal> averageLine,LineResult.LineContent<BigDecimal> standardDeviationLine){
+        BigDecimal max=BigDecimal.valueOf(0);
+        BigDecimal min=BigDecimal.valueOf(Integer.MAX_VALUE);
+        BigDecimal total=BigDecimal.valueOf(0);
+        ArrayList<BigDecimal> allContinueDose = new ArrayList<>();
+        for (BigDecimal source : sources) {
+            if(source!=null){
+                max=source.compareTo(max)>0?source:max;
+                min=source.compareTo(min)<0?source:min;
+                total=total.add(source);
+                allContinueDose.add(source);
+            }
+        }
+
+        maxLine.addValue(max);
+        minLine.addValue(min.compareTo(BigDecimal.valueOf(Integer.MAX_VALUE))==0?BigDecimal.ZERO:min);
+        BigDecimal average = total.divide(BigDecimal.valueOf(CollectionUtil.size(sources)), 2, RoundingMode.HALF_UP);
+        averageLine.addValue(average);
+        standardDeviationLine.addValue(computeStandardDeviation(allContinueDose,average));
+    }
+    @Override
+    public TableResult handleTable(List<CombineResult> results, StatsTimeUnit unit) {
+        return null;
+    }
+
+}

+ 190 - 0
coffee-system/src/main/java/com/coffee/bus/stats/analyse/PcaStatsAnalyse.java

@@ -0,0 +1,190 @@
+package com.coffee.bus.stats.analyse;
+
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.coffee.bus.enums.StatsAnalyseEnum;
+import com.coffee.bus.enums.StatsTimeUnit;
+import com.coffee.bus.service.LocalBusInfusionHistoryService;
+import com.coffee.bus.service.dto.CombineQuery;
+import com.coffee.bus.service.dto.CombineResult;
+import com.coffee.bus.stats.CommonStats;
+import com.coffee.bus.stats.entity.*;
+import com.coffee.bus.stats.enums.PieEnum;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName PcaStatsAnalyse.java
+ * @Description 自控分析统计
+ * @createTime 2022年06月07日 15:09:00
+ */
+@Component
+@AllArgsConstructor
+public class PcaStatsAnalyse implements CommonStats<CombineResult> {
+    private final LocalBusInfusionHistoryService infusionHistoryService;
+    @Override
+    public List<CombineResult> queryResult(CombineQuery query) {
+        return infusionHistoryService.queryStatsAnal(query);
+    }
+
+    @Override
+    public StatsAnalyseEnum getId() {
+        return StatsAnalyseEnum.pca;
+    }
+
+    @Override
+    public List<PieResult> handlePie(List<CombineResult> results) {
+        PieResult result = PieResult.of(PieEnum.total.name(),PieEnum.total.getText());
+        long total=0;
+        long pcaValidTotalCount=0;
+        long pcaInvalidTotalCount=0;
+        for (CombineResult combineResult : results) {
+            Integer validCount = Optional.ofNullable(combineResult.getPcaValidCount()).orElse(0);
+            Integer invalidCount =Optional.ofNullable( combineResult.getPcaInvalidCount()).orElse(0);
+            pcaValidTotalCount=pcaValidTotalCount+validCount;
+            pcaInvalidTotalCount=pcaInvalidTotalCount+invalidCount;
+            total=total+validCount+invalidCount;
+        }
+        result.setContent(Arrays.asList( PieResult.PieContent.of("自控有效数",pcaValidTotalCount),
+                PieResult.PieContent.of("自控无效数",pcaInvalidTotalCount)));
+        return Arrays.asList(result);
+    }
+
+    @Override
+    public List<LineResult> handleLine(List<CombineResult> results, StatsTimeUnit unit) {
+        //根据时间对结果进行区分统计
+        Map<String, List<CombineResult>> groupByTime = groupByTime(results, unit);
+        //获取所有镇痛信息
+        List<LineResult> result = new ArrayList<>();
+        result.add(countLine(groupByTime));
+        result.add(capitaLine(groupByTime));
+        //比率
+        return result;
+    }
+
+    /**
+     * 描述: 人均次数
+     * @author lifang
+     * @date 2022/6/7 15:50
+     * @param groupByTime
+     * @return LineResult
+     */
+    private LineResult capitaLine(Map<String, List<CombineResult>> groupByTime) {
+        LineResult<BigDecimal> result = new LineResult<>();
+        List<String> time = new ArrayList<>();
+        result.setTime(time);
+        result.setId("capita");
+        result.setDescription("此数据为 人均次数 【次数统计】");
+        LineResult.LineContent<BigDecimal> perCapitaValidLine = new LineResult.LineContent<>("人均有效次数");
+        LineResult.LineContent<BigDecimal> perCapitaInValidLine = new LineResult.LineContent<>("人均无效次数");
+        LineResult.LineContent<BigDecimal> perCapitaTotalValidLine = new LineResult.LineContent<>("人均总次数");
+        groupByTime.forEach((timeRange,combineResults)->{
+            long total=0;
+            long pcaValidTotalCount=0;
+            long pcaInvalidTotalCount=0;
+
+            for (CombineResult combineResult : combineResults) {
+                Integer validCount = Optional.ofNullable(combineResult.getPcaValidCount()).orElse(0);
+                Integer invalidCount =Optional.ofNullable( combineResult.getPcaInvalidCount()).orElse(0);
+                pcaValidTotalCount=pcaValidTotalCount+validCount;
+                pcaInvalidTotalCount=pcaInvalidTotalCount+invalidCount;
+                total=total+validCount+invalidCount;
+            }
+
+            BigDecimal patientCount = BigDecimal.valueOf(combineResults.stream().map(CombineResult::getPatientCode).distinct().count());
+            perCapitaTotalValidLine.addValue(BigDecimal.valueOf(total).divide(patientCount,2, RoundingMode.HALF_UP));
+            perCapitaInValidLine.addValue(BigDecimal.valueOf(pcaValidTotalCount).divide(patientCount,2, RoundingMode.HALF_UP));
+            perCapitaValidLine.addValue(BigDecimal.valueOf(pcaInvalidTotalCount).divide(patientCount,2, RoundingMode.HALF_UP));
+            time.add(timeRange);
+        });
+        result.setContent(Arrays.asList(perCapitaValidLine,perCapitaInValidLine,perCapitaTotalValidLine));
+        return result;
+    }
+
+    /**
+     * 描述: 自控次数统计图
+     * @author lifang
+     * @date 2022/6/7 15:35
+     * @param groupByTime
+     * @return LineResult
+     */
+    private LineResult countLine(Map<String, List<CombineResult>> groupByTime) {
+        LineResult<Long> result = new LineResult<>();
+        LineResult.LineContent<Long> validCountLine = new LineResult.LineContent<>("自控有效次数");
+        LineResult.LineContent<Long> invalidCountLine = new LineResult.LineContent<>("自控无效次数");
+        List<String> time = new ArrayList<>();
+        result.setTime(time);
+        result.setId("count");
+        result.setDescription("此数据为 自控次数 【次数统计】");
+        //根据时间区间对pca次数进行统计
+        groupByTime.forEach((timeRange,combineResults)->{
+            long pcaValidTotalCount=0;
+            long pcaInvalidTotalCount=0;
+            for (CombineResult combineResult : combineResults) {
+                Integer validCount = Optional.ofNullable(combineResult.getPcaValidCount()).orElse(0);
+                Integer invalidCount =Optional.ofNullable( combineResult.getPcaInvalidCount()).orElse(0);
+                pcaValidTotalCount=pcaValidTotalCount+validCount;
+                pcaInvalidTotalCount=pcaInvalidTotalCount+invalidCount;
+            }
+            validCountLine.addValue(pcaValidTotalCount);
+            invalidCountLine.addValue(pcaInvalidTotalCount);
+            time.add(timeRange);
+        });
+        result.setContent(Arrays.asList(validCountLine,invalidCountLine));
+        return result;
+    }
+
+    @Override
+    public TableResult handleTable(List<CombineResult> results, StatsTimeUnit unit) {
+        TableResult result = new TableResult();
+        //根据时间对结果进行区分统计
+        Map<String, List<CombineResult>> groupByTime = groupByTime(results, unit);
+        List<StatsColumn> allColumn = Arrays.asList(
+                StatsColumn.of("时间","time"),
+                StatsColumn.of("输注总数","输注总数"),
+                StatsColumn.of("自控总数","自控总数"),
+                StatsColumn.of("自控有效数","自控无效数"),
+                StatsColumn.of("人均自控数","人均自控数"),
+                StatsColumn.of("人均自控有效数","人均自控有效数"),
+                StatsColumn.of("人均自控无效数","人均自控无效数"));
+        List<Map<String, Object>> contents = new ArrayList<>();
+        result.setContent(contents);
+        result.setColumn(allColumn);
+        //根据时间区间对镇痛方式进行统计
+        groupByTime.forEach((timeRange,combineResults)->{
+            LinkedHashMap<String, Object> contentValues = new LinkedHashMap<>();
+            contentValues.put("time",timeRange);
+            long total=0;
+            long pcaValidTotalCount=0;
+            long pcaInvalidTotalCount=0;
+
+            for (CombineResult combineResult : combineResults) {
+                Integer validCount = Optional.ofNullable(combineResult.getPcaValidCount()).orElse(0);
+                Integer invalidCount =Optional.ofNullable( combineResult.getPcaInvalidCount()).orElse(0);
+                pcaValidTotalCount=pcaValidTotalCount+validCount;
+                pcaInvalidTotalCount=pcaInvalidTotalCount+invalidCount;
+                total=total+validCount+invalidCount;
+            }
+
+            contentValues.put("输注总数", CollectionUtil.size(combineResults));
+            contentValues.put("自控总数",total);
+            contentValues.put("自控有效数",pcaValidTotalCount);
+            contentValues.put("自控无效数",pcaInvalidTotalCount);
+            BigDecimal patientCount = BigDecimal.valueOf(combineResults.stream().map(CombineResult::getPatientCode).distinct().count());
+
+            contentValues.put("人均自控数",BigDecimal.valueOf(total).divide(patientCount,2, RoundingMode.HALF_UP));
+            contentValues.put("人均自控有效数",BigDecimal.valueOf(pcaValidTotalCount).divide(patientCount,2, RoundingMode.HALF_UP));
+            contentValues.put("人均自控无效数",BigDecimal.valueOf(pcaInvalidTotalCount).divide(patientCount,2, RoundingMode.HALF_UP));
+
+            contents.add(contentValues);
+        });
+
+        return result;
+    }
+}

+ 200 - 0
coffee-system/src/main/java/com/coffee/bus/stats/entity/AlarmTotalPieResult.java

@@ -0,0 +1,200 @@
+package com.coffee.bus.stats.entity;
+
+import com.coffee.bus.enums.DeviceAlarmEnum;
+import com.coffee.bus.enums.DeviceStatusEnum;
+import com.coffee.bus.enums.FlowStatusEnum;
+import com.coffee.bus.service.dto.CombineAlarmResult;
+import com.coffee.bus.stats.entity.PieResult.*;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.stereotype.Service;
+
+import java.util.HashMap;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AlarmTotalPieResult.java
+ * @Description TODO
+ * @createTime 2022年06月07日 10:26:00
+ */
+public class AlarmTotalPieResult extends HashMap<String,PieContent> {
+    @ApiModelProperty(value = "堵塞报警",readOnly = true)
+    private PieContent jam;
+    @ApiModelProperty(value = "极限报警",readOnly = true)
+    private PieContent limit;
+    @ApiModelProperty(value = "未装药盒报警",readOnly = true)
+    private PieContent noBox;
+    @ApiModelProperty(value = "电机失控报警",readOnly = true)
+    private PieContent outOfControl;
+    @ApiModelProperty(value = "镇痛不足报警",readOnly = true)
+    private PieContent analgesicPoor;
+    @ApiModelProperty(value = "不在服务区报警",readOnly = true)
+    private PieContent noSignal;
+    @ApiModelProperty(value = "机械故障报警",readOnly = true)
+    private PieContent machine;
+    @ApiModelProperty(value = "电量耗尽报警",readOnly = true)
+    private PieContent lowBattery;
+    @ApiModelProperty(value = "气泡无液报警",readOnly = true)
+    private PieContent bubble;
+    @ApiModelProperty(value = "低输注状态",readOnly = true)
+    private PieContent lowInfusion;
+
+    @ApiModelProperty(value = "该报警集总数",readOnly = true)
+    @Getter
+    private long total=0;
+
+    @ApiModelProperty(value = "输注总数",readOnly = true)
+    @Getter
+    @Setter
+    private long infusionCount;
+
+    public PieContent getJam() {
+        return this.computeIfAbsent("jam",k->PieContent.of("堵塞",0L));
+    }
+
+    public void incrementJam() {
+        incrementValue(this.getJam());
+    }
+
+    public PieContent getLimit() {
+        return this.computeIfAbsent("limit",k->PieContent.of("极限",0L));
+    }
+
+    public void incrementLimit() {
+        incrementValue(this.getLimit());
+    }
+
+    public PieContent getNoBox() {
+        return this.computeIfAbsent("noBox",k->PieContent.of("未装药盒",0L));
+    }
+
+    public void incrementNoBox() {
+        incrementValue(this.getNoBox());
+    }
+
+    public PieContent getOutOfControl() {
+        return this.computeIfAbsent("outOfControl",k->PieContent.of("电机失控",0L));
+    }
+
+    public void incrementOutOfControl() {
+        incrementValue(this.getOutOfControl());
+    }
+
+
+    public PieContent getAnalgesicPoor() {
+        return this.computeIfAbsent("analgesicPoor",k->PieContent.of("镇痛不足",0L));
+    }
+
+
+    public void incrementAnalgesicPoor() {
+        incrementValue(this.getAnalgesicPoor());
+    }
+
+    public PieContent getNoSignal() {
+        return this.computeIfAbsent("noSignal",k->PieContent.of("不在服务区",0L));
+    }
+
+    public void incrementNoSignal() {
+        incrementValue(this.getNoSignal());
+    }
+
+
+    public PieContent getMachine() {
+        return this.computeIfAbsent("machine",k->PieContent.of("机械故障",0L));
+    }
+
+    public void incrementMachine() {
+        incrementValue(this.getMachine());
+    }
+
+    public PieContent getLowBattery() {
+        return this.computeIfAbsent("lowBattery",k->PieContent.of("电量耗尽",0L));
+    }
+
+
+    public void incrementLowBattery() {
+        incrementValue(this.getLowBattery());
+    }
+
+    public PieContent getBubble() {
+        return this.computeIfAbsent("bubble",k->PieContent.of("气泡无液",0L));
+    }
+
+
+    public void incrementBubble() {
+        incrementValue(this.getBubble());
+    }
+
+    public PieContent getLowInfusion() {
+        return this.computeIfAbsent("lowInfusion",k->PieContent.of("低输注状态",0L));
+    }
+
+    public void incrementLowInfusion() {
+        incrementValue(this.getLowInfusion());
+    }
+
+    public PieContent getInfusionMax() {
+        return this.computeIfAbsent("infusionMax",k->PieContent.of("返厂维护",0L));
+    }
+
+    public void incrementInfusionMax() {
+        incrementValue(this.getInfusionMax());
+    }
+
+    private void incrementValue(PieContent pieContent){
+        pieContent.setValue(pieContent.getValue()+1);
+        ++total;
+    }
+
+
+
+
+    public void handle(CombineAlarmResult alarmResult) {
+        if (Boolean.TRUE.equals(alarmResult.getIsAlarm())) {
+            DeviceAlarmEnum alarm = alarmResult.getAlarm();
+            switch (alarm){
+                case Jam:
+                    incrementJam();
+                    break;
+                case Limit:
+                    incrementLimit();
+                    break;
+                case NotBox:
+                    incrementNoBox();
+                    break;
+                case Machine:
+                    incrementMachine();
+                    break;
+                case OutOfControl:
+                    incrementOutOfControl();
+                    break;
+                case LowBattery:
+                    incrementLowBattery();
+                    break;
+                case Bubble:
+                    incrementBubble();
+                    break;
+                case InfusionMax:
+                    incrementInfusionMax();
+                    break;
+                default:break;
+            }
+        }else {
+            Boolean warnAnalgesicPoor = alarmResult.getWarnAnalgesicPoor();
+            if (Boolean.TRUE.equals(warnAnalgesicPoor)) {
+                incrementAnalgesicPoor();
+            }
+            DeviceStatusEnum runState = alarmResult.getRunState();
+            if (DeviceStatusEnum.NoSignal.equals(runState)) {
+                incrementNoSignal();
+            }
+            FlowStatusEnum warnFlow = alarmResult.getWarnFlow();
+            if (FlowStatusEnum.Lowest.equals(warnFlow)) {
+                incrementLowInfusion();
+            }
+
+        }
+    }
+}

+ 48 - 0
coffee-system/src/main/java/com/coffee/bus/stats/entity/LineResult.java

@@ -0,0 +1,48 @@
+package com.coffee.bus.stats.entity;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.*;
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName LineResult.java
+ * @Description 折线图数据结果
+ * @createTime 2022年06月06日 09:53:00
+ */
+@Data
+@AllArgsConstructor(staticName = "of")
+@NoArgsConstructor
+public class LineResult<V> {
+    private String id;
+    private List<LineContent<V>> content;
+    private List<String> time;
+    private String description;
+
+
+    @Data
+    public static class  LineContent<V>{
+        private String id;
+        private List<V> value;
+        @ApiModelProperty("最大值")
+        private boolean max;
+        @ApiModelProperty("最小值")
+        private boolean min;
+        @ApiModelProperty("方差")
+        private boolean variance;
+        @ApiModelProperty("平均值")
+        private boolean average;
+
+        public LineContent(String id) {
+            this.id = id;
+            this.value=new ArrayList<>();
+        }
+
+        public void addValue(V v){
+            this.value.add(v);
+        }
+    }
+}

+ 42 - 0
coffee-system/src/main/java/com/coffee/bus/stats/entity/PieResult.java

@@ -0,0 +1,42 @@
+package com.coffee.bus.stats.entity;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.util.*;
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName PieResult.java
+ * @Description 饼图数据结果
+ * @createTime 2022年06月06日 09:14:00
+ */
+@Data
+@AllArgsConstructor(staticName = "of")
+public class PieResult {
+    @ApiModelProperty("饼图id,用以判别不同的饼图类型, 如:堵塞报警")
+    private String id;
+
+    private List<PieContent> content;
+
+    private String description;
+
+    public static PieResult of(String id,String description){
+        return of(id,new ArrayList<>(),description);
+    }
+
+    public void addContent(String name,Long value){
+        content.add(PieContent.of(name,value));
+    }
+
+    public void addContent(String name,Integer value){
+        content.add(PieContent.of(name,Long.valueOf(value)));
+    }
+    @Data
+    @AllArgsConstructor(staticName = "of")
+    public static class  PieContent{
+        private String name;
+        private Long value;
+    }
+}

+ 20 - 0
coffee-system/src/main/java/com/coffee/bus/stats/entity/StatsAnalyseResult.java

@@ -0,0 +1,20 @@
+package com.coffee.bus.stats.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+import java.util.*;
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName StatsAnalyseResult.java
+ * @Description TODO
+ * @createTime 2022年06月06日 21:23:00
+ */
+@Data
+@AllArgsConstructor(staticName = "of")
+public class StatsAnalyseResult {
+    private List<PieResult> pie;
+    private List<LineResult> line;
+    private TableResult table;
+}

+ 42 - 0
coffee-system/src/main/java/com/coffee/bus/stats/entity/StatsColumn.java

@@ -0,0 +1,42 @@
+package com.coffee.bus.stats.entity;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName StatsColumn.java
+ * @Description 统计列名称
+ * @createTime 2022年06月06日 22:20:00
+ */
+@Data
+@AllArgsConstructor(staticName = "of")
+public class StatsColumn {
+    @ApiModelProperty(value = "列名",hidden = true)
+    private String title;
+    @ApiModelProperty(value = "列名属性名称",hidden = true)
+    private String dataIndex;
+    @JsonIgnore
+    @ApiModelProperty(value = "是否为比率",hidden = true)
+    private boolean isRatio;
+
+    public static StatsColumn of(String title, String dataIndex) {
+        return of(title,dataIndex,false);
+    }
+
+    public String getTitle() {
+        return isRatio?title+"比率":title;
+    }
+
+    public String getDataIndex() {
+        return isRatio?dataIndex+"比率":dataIndex;
+    }
+
+
+    public static String ratioColumnName(String title){
+        return title+"比率";
+    }
+}

+ 27 - 0
coffee-system/src/main/java/com/coffee/bus/stats/entity/TableResult.java

@@ -0,0 +1,27 @@
+package com.coffee.bus.stats.entity;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.*;
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName TableResult.java
+ * @Description 图表结果
+ * @createTime 2022年06月06日 10:05:00
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class TableResult {
+
+    @ApiModelProperty("图表列名")
+    private List<StatsColumn> column;
+
+    @ApiModelProperty("图表内容 时间从大到小 一个map是一行数据")
+    private List<Map<String,Object>> content;
+
+}

+ 18 - 0
coffee-system/src/main/java/com/coffee/bus/stats/enums/PieEnum.java

@@ -0,0 +1,18 @@
+package com.coffee.bus.stats.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName PieEnum.java
+ * @Description 饼图类型
+ * @createTime 2022年06月06日 10:19:00
+ */
+@AllArgsConstructor
+@Getter
+public enum  PieEnum {
+    total("总量饼图");
+    private String text;
+}

+ 59 - 0
coffee-system/src/main/java/com/coffee/bus/stats/report/GenderStatsReport.java

@@ -0,0 +1,59 @@
+package com.coffee.bus.stats.report;
+
+import com.coffee.bus.enums.StatsAnalyseEnum;
+import com.coffee.bus.enums.StatsTimeUnit;
+import com.coffee.bus.service.LocalBusInfusionHistoryService;
+import com.coffee.bus.service.dto.CombineQuery;
+import com.coffee.bus.service.dto.CombineResult;
+import com.coffee.bus.stats.CommonStats;
+import com.coffee.bus.stats.entity.LineResult;
+import com.coffee.bus.stats.entity.PieResult;
+import com.coffee.bus.stats.entity.TableResult;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName GenderStatsReport.java
+ * @Description TODO
+ * @createTime 2022年06月08日 09:09:00
+ */
+@AllArgsConstructor
+@Service
+public class GenderStatsReport implements CommonStats<CombineResult> {
+    private final LocalBusInfusionHistoryService infusionHistoryService;
+    @Override
+    public List<CombineResult> queryResult(CombineQuery query) {
+        List<CombineResult> combineResults = infusionHistoryService.queryStatsAnal(query);
+        //根据临床id进行去重操作
+        Map<String, CombineResult> clinicIdMap = new HashMap<>();
+        combineResults.forEach(result->clinicIdMap.computeIfAbsent(result.getClinicId(),k->result));
+        return new ArrayList<>(clinicIdMap.values());
+    }
+
+    @Override
+    public StatsAnalyseEnum getId() {
+        return StatsAnalyseEnum.gender;
+    }
+
+    @Override
+    public List<PieResult> handlePie(List<CombineResult> results) {
+        return null;
+    }
+
+    @Override
+    public List<LineResult> handleLine(List<CombineResult> results, StatsTimeUnit unit) {
+        return null;
+    }
+
+    @Override
+    public TableResult handleTable(List<CombineResult> results, StatsTimeUnit unit) {
+        return null;
+    }
+}

+ 5 - 4
coffee-system/src/main/java/com/coffee/bus/websocket/listener/DeviceInfoListener.java

@@ -298,10 +298,11 @@ public class DeviceInfoListener {
                 infusionModifyService.save(modify);
             }
             device.setInfusionModifyId(modify.getId());
-            if (DeviceStatusEnum.Shutdown.equals(device.getRunState())) {
-                //设备关机后,查看用户是否存在其他正在进行的输注
-                infusionHistoryService.deviceShutDown(device.getInfusionId(),device.getPatientCode(),device.getTenantId());
-            }
+            //已输注为主对无泵进行判断
+//            if (DeviceStatusEnum.Shutdown.equals(device.getRunState())) {
+//                //设备关机后,查看用户是否存在其他正在进行的输注
+//                infusionHistoryService.deviceShutDown(device.getInfusionId(),device.getPatientCode(),device.getTenantId());
+//            }
         }
     }
 

+ 7 - 6
coffee-system/src/main/resources/mapper/bus/BusDeviceMapper.xml

@@ -38,9 +38,9 @@
         d.id as id,
         d.device_id as device_id,
         d.alias as alias,
-        d.type as type,
-        r.run_state as run_state,
-        r.alarm as alarm,
+        ifnull(i.type,3) as type,
+        i.run_state as run_state,
+        i.alarm as alarm,
         d.config as config,
         d.sim_iccid as sim_iccid,
         d.sim_mno as sim_mno,
@@ -65,8 +65,9 @@
             </if>
         </where>
         ) AS d
-        LEFT JOIN (select * from bus_device_running
+        LEFT JOIN (select * from bus_infusion_history
         <where>
+            finished=0
             <if test="query.runStates != null and query.runStates.size > 0">
                 and run_state in
                 <foreach item="state" index="index" collection="query.runStates" open="(" separator="," close=")">
@@ -80,8 +81,8 @@
                 </foreach>
             </if>
         </where>
-        ) as r
-        on r.device_id=d.device_id
+        ) as i
+        on i.device_id=d.device_id
         order by d.create_time desc
     </select>
 </mapper>

+ 311 - 1
coffee-system/src/main/resources/mapper/bus/BusInfusionHistoryMapper.xml

@@ -67,6 +67,21 @@
         <result column="entrust" property="entrust"/>
     </resultMap>
 
+
+    <resultMap id="combineAlarmResult" type="com.coffee.bus.service.dto.CombineAlarmResult">
+        <result column="device_type" property="type"/>
+        <result column="infusion_id" property="id"/>
+        <result column="infusion_start_time" property="infusionStartTime"/>
+        <result column="cause" property="cause"/>
+        <result column="alarm" property="isAlarm"/>
+        <result column="run_state" property="runState"/>
+        <result column="alarm_state" property="alarm"/>
+        <result column="warn_will_finished" property="warnWillFinished"/>
+        <result column="warn_analgesic_poor" property="warnAnalgesicPoor"/>
+        <result column="warn_low_battery" property="warnLowBattery"/>
+        <result column="warn_flow" property="warnFlow"/>
+    </resultMap>
+
     <select id="currentInClinic" resultType="com.coffee.bus.entity.BusInfusionHistoryEntity">
         select d.alias,i.* from
             (select * from bus_infusion_history where clinic_id=#{clinicId} ORDER BY start_time desc limit 1) as i
@@ -132,7 +147,6 @@
         c.weight as weight,
         c.`height` as height,
         c.ana_doctor as ana_doctor,
-        c.patient_age as patient_age,
         c.ana_type as ana_type,
         c.anal_type as anal_type,
         c.surgery_doctor as surgery_doctor,
@@ -268,4 +282,300 @@
         on i.clinic_id=c.id
     </select>
 
+
+
+    <select id="queryStatsAlarm" resultMap="combineAlarmResult">
+        select
+        i.type as device_type,
+        i.id as infusion_id,
+        i.start_time as infusion_start_time,
+        a.cause as cause,
+        i.patient_code as patient_code,
+        a.alarm as alarm,
+        a.run_state as run_state,
+        a.alarm_state as alarm_state,
+        a.warn_will_finished as warn_will_finished,
+        a.warn_low_battery as warn_low_battery,
+        a.warn_analgesic_poor as warn_analgesic_poor,
+        a.warn_flow as warn_flow
+        from (select * from bus_infusion_history
+        <where>
+            <if test="query.startTimeRange != null and query.startTimeRange.size > 0">
+                and start_time &gt;  #{query.startTimeRange[0]}
+                <if test="query.startTimeRange.size >1 ">
+                    and start_time &lt;  #{query.startTimeRange[1]}
+                </if>
+            </if>
+
+            <if test="query.undoTimeRange != null and query.undoTimeRange.size > 0">
+                and undo_time &gt;  #{query.undoTimeRange[0]}
+                <if test="query.startTimeRange.size >1 ">
+                    and undo_time &lt;  #{query.undoTimeRange[1]}
+                </if>
+            </if>
+
+            <if test="query.deviceType != null">
+                and device_type =  #{query.deviceType}
+            </if>
+
+            <if test="query.deviceId != null">
+                and device_id like concat('%',#{query.deviceId},'%')
+            </if>
+
+            <if test="query.validPcaCountRange != null and query.validPcaCountRange.size > 0">
+                and pca_valid_count &gt;  #{query.validPcaCountRange[0]}
+                <if test="query.validPcaCountRange.size >1 ">
+                    and pca_valid_count &lt;  #{query.validPcaCountRange[1]}
+                </if>
+            </if>
+
+            <if test="query.inValidPcaCountRange != null and query.inValidPcaCountRange.size > 0">
+                and pca_invalid_count &gt;  #{query.inValidPcaCountRange[0]}
+                <if test="query.inValidPcaCountRange.size >1 ">
+                    and pca_invalid_count &lt;  #{query.inValidPcaCountRange[1]}
+                </if>
+            </if>
+
+            <choose>
+                <when test="query.warnWillFinished != false or query.warnAnalgesicPoor != false or  query.warnLowBattery != false">
+                    and (
+                    <choose>
+                        <when test="query.warnWillFinished != false">warn_will_finished=1</when>
+                        <otherwise>warn_will_finished!=1</otherwise>
+                    </choose>
+                    <if test="query.warnAnalgesicPoor != false">or warn_analgesic_poor=1 </if>
+                    <if test="query.warnLowBattery != false"> or warn_low_battery=1 </if>
+                    <if test="query.warnFlow !=null">
+                        or warn_flow= #{query.warnFlow}
+                    </if>
+                    )
+                </when>
+                <otherwise>
+                    <if test="query.warnFlow !=null">
+                        and warn_flow= #{query.warnFlow}
+                    </if>
+                </otherwise>
+            </choose>
+
+        </where>
+        ) as i
+        JOIN (select * from bus_clinic
+        <where>
+            <if test="query.patientCode != null">
+                and patient_code like concat('%',#{query.patientCode},'%')
+            </if>
+
+            <if test="query.patientName != null">
+                and patient_name like concat('%',#{query.patientName},'%')
+            </if>
+
+            <if test="query.patientGender != null">
+                and patient_gender =#{query.patientGender}
+            </if>
+
+            <if test="query.ageRange != null and query.ageRange.size > 0">
+                and patient_age &gt;  #{query.ageRange[0]}
+                <if test="query.ageRange.size >1 ">
+                    and patient_age &lt;  #{query.ageRange[1]}
+                </if>
+            </if>
+
+            <if test="query.weightRange != null and query.weightRange.size > 0">
+                and weight &gt;  #{query.weightRange[0]}
+                <if test="query.weightRange.size >1 ">
+                    and weight &lt;  #{query.weightRange[1]}
+                </if>
+            </if>
+
+            <if test="query.asa != null">
+                and asa like concat('%',#{query.asa},'%')
+            </if>
+
+            <if test="query.ward != null">
+                and ward like concat('%',#{query.ward},'%')
+            </if>
+
+            <if test="query.bedNo != null">
+                and bed_no like concat('%',#{query.bedNo},'%')
+            </if>
+
+            <if test="query.anaDoctor != null">
+                and ana_doctor like concat('%',#{query.anaDoctor},'%')
+            </if>
+
+
+            <if test="query.anaType != null">
+                and ana_type like concat('%',#{query.anaType},'%')
+            </if>
+
+            <if test="query.analType != null">
+                and anal_type like concat('%',#{query.analType},'%')
+            </if>
+
+            <if test="query.drugName != null">
+                and formula like concat('%',#{query.drugName},'%')
+            </if>
+
+            <if test="query.surgeryDoctor != null">
+                and surgery_doctor like concat('%',#{query.surgeryDoctor},'%')
+            </if>
+
+            <if test="query.surgeryName != null">
+                and surgery_name like concat('%',#{query.surgeryName},'%')
+            </if>
+        </where>
+        ) as c
+        on i.clinic_id=c.id
+        JOIN bus_device_alarm a on a.infusion_id = i.id
+    </select>
+
+    <select id="queryStatsAnal" resultMap="combineResult">
+        select
+        i.id as infusion_id,
+        i.device_id as device_id,
+        i.pca_valid_count as pca_valid_count,
+        i.pca_invalid_count as pca_invalid_count,
+        i.pca_total_count as pca_total_count,
+        i.start_time as infusion_start_time,
+        i.continue_dose as continue_dose,
+        i.input_dose as input_dose,
+        i.append_dose as append_dose,
+        i.self_control_lock_time as self_control_lock_time,
+        i.patient_code as patient_code,
+        c.id as clinic_id,
+        c.asa as asa,
+        c.patient_gender as patient_gender,
+        c.`patient_age` as patient_age,
+        c.anal_type as anal_type,
+        c.weight as weight,
+        c.`height` as height
+        from (select * from bus_infusion_history
+        <where>
+            <if test="query.startTimeRange != null and query.startTimeRange.size > 0">
+                and start_time &gt;  #{query.startTimeRange[0]}
+                <if test="query.startTimeRange.size >1 ">
+                    and start_time &lt;  #{query.startTimeRange[1]}
+                </if>
+            </if>
+
+            <if test="query.undoTimeRange != null and query.undoTimeRange.size > 0">
+                and undo_time &gt;  #{query.undoTimeRange[0]}
+                <if test="query.startTimeRange.size >1 ">
+                    and undo_time &lt;  #{query.undoTimeRange[1]}
+                </if>
+            </if>
+
+            <if test="query.deviceType != null">
+                and device_type =  #{query.deviceType}
+            </if>
+
+            <if test="query.deviceId != null">
+                and device_id like concat('%',#{query.deviceId},'%')
+            </if>
+
+            <if test="query.validPcaCountRange != null and query.validPcaCountRange.size > 0">
+                and pca_valid_count &gt;  #{query.validPcaCountRange[0]}
+                <if test="query.validPcaCountRange.size >1 ">
+                    and pca_valid_count &lt;  #{query.validPcaCountRange[1]}
+                </if>
+            </if>
+
+            <if test="query.inValidPcaCountRange != null and query.inValidPcaCountRange.size > 0">
+                and pca_invalid_count &gt;  #{query.inValidPcaCountRange[0]}
+                <if test="query.inValidPcaCountRange.size >1 ">
+                    and pca_invalid_count &lt;  #{query.inValidPcaCountRange[1]}
+                </if>
+            </if>
+
+            <choose>
+                <when test="query.warnWillFinished != false or query.warnAnalgesicPoor != false or  query.warnLowBattery != false">
+                    and (
+                    <choose>
+                        <when test="query.warnWillFinished != false">warn_will_finished=1</when>
+                        <otherwise>warn_will_finished!=1</otherwise>
+                    </choose>
+                    <if test="query.warnAnalgesicPoor != false">or warn_analgesic_poor=1 </if>
+                    <if test="query.warnLowBattery != false"> or warn_low_battery=1 </if>
+                    <if test="query.warnFlow !=null">
+                        or warn_flow= #{query.warnFlow}
+                    </if>
+                    )
+                </when>
+                <otherwise>
+                    <if test="query.warnFlow !=null">
+                        and warn_flow= #{query.warnFlow}
+                    </if>
+                </otherwise>
+            </choose>
+
+        </where>
+        ) as i
+        JOIN (select * from bus_clinic
+        <where>
+            <if test="query.patientCode != null">
+                and patient_code like concat('%',#{query.patientCode},'%')
+            </if>
+
+            <if test="query.patientName != null">
+                and patient_name like concat('%',#{query.patientName},'%')
+            </if>
+
+            <if test="query.patientGender != null">
+                and patient_gender =#{query.patientGender}
+            </if>
+
+            <if test="query.ageRange != null and query.ageRange.size > 0">
+                and patient_age &gt;  #{query.ageRange[0]}
+                <if test="query.ageRange.size >1 ">
+                    and patient_age &lt;  #{query.ageRange[1]}
+                </if>
+            </if>
+
+            <if test="query.weightRange != null and query.weightRange.size > 0">
+                and weight &gt;  #{query.weightRange[0]}
+                <if test="query.weightRange.size >1 ">
+                    and weight &lt;  #{query.weightRange[1]}
+                </if>
+            </if>
+
+            <if test="query.asa != null">
+                and asa like concat('%',#{query.asa},'%')
+            </if>
+
+            <if test="query.ward != null">
+                and ward like concat('%',#{query.ward},'%')
+            </if>
+
+            <if test="query.bedNo != null">
+                and bed_no like concat('%',#{query.bedNo},'%')
+            </if>
+
+            <if test="query.anaDoctor != null">
+                and ana_doctor like concat('%',#{query.anaDoctor},'%')
+            </if>
+
+
+            <if test="query.anaType != null">
+                and ana_type like concat('%',#{query.anaType},'%')
+            </if>
+
+            <if test="query.analType != null">
+                and anal_type like concat('%',#{query.analType},'%')
+            </if>
+
+            <if test="query.drugName != null">
+                and formula like concat('%',#{query.drugName},'%')
+            </if>
+
+            <if test="query.surgeryDoctor != null">
+                and surgery_doctor like concat('%',#{query.surgeryDoctor},'%')
+            </if>
+
+            <if test="query.surgeryName != null">
+                and surgery_name like concat('%',#{query.surgeryName},'%')
+            </if>
+        </where>
+        ) as c
+        on i.clinic_id=c.id
+    </select>
 </mapper>

+ 8 - 8
coffee-system/src/main/resources/mapper/bus/BusPatientMapper.xml

@@ -216,12 +216,6 @@
         (select * from bus_infusion_history
         <where>
             is_undo='0'
-            <if test="query.wards != null and query.wards.size > 0">
-                and ward in
-                <foreach item="ward" index="index" collection="query.wards" open="(" separator="," close=")">
-                    #{ward, jdbcType=VARCHAR}
-                </foreach>
-            </if>
             <if test="query.types != null and query.types.size > 0">
                 and type in
                 <foreach item="type" index="index" collection="query.types" open="(" separator="," close=")">
@@ -283,6 +277,12 @@
         join (select * from bus_clinic
         <where>
             finished=0
+            <if test="query.wards != null and query.wards.size > 0">
+                and ward in
+                <foreach item="ward" index="index" collection="query.wards" open="(" separator="," close=")">
+                    #{ward, jdbcType=VARCHAR}
+                </foreach>
+            </if>
             <if test="query.timeRange != null and query.timeRange.size >0">
                 and monitor_start_time &gt; #{query.timeRange[0]} and  monitor_start_time &lt; #{query.timeRange[1]}
             </if>
@@ -295,7 +295,7 @@
             <where>
                 (1=0
                 <if test="query.bedNo!=null">
-                    or i.bed_no LIKE concat('%', #{query.bedNo}, '%')
+                    or c.bed_no LIKE concat('%', #{query.bedNo}, '%')
                 </if>
                 <if test="query.name!=null">
                     or c.`patient_name` LIKE concat('%', #{query.name}, '%')
@@ -310,7 +310,7 @@
                     or c.`surgery_name` like concat('%',#{query.surgeName}, '%')
                 </if>
                 <if test="query.gender!=null">
-                    or c.gender LIKE concat('%', #{query.gender}, '%')
+                    or c.patient_gender LIKE concat('%', #{query.gender}, '%')
                 </if>
                 <if test="query.anaType!=null">
                     or c.ana_type LIKE concat('%', #{query.anaType}, '%')