A17404李放 3 anni fa
parent
commit
9211c80b41
34 ha cambiato i file con 2422 aggiunte e 267 eliminazioni
  1. 6 0
      coffee-common/src/main/java/com/coffee/common/result/ResultCode.java
  2. 1 1
      coffee-system/src/main/java/com/coffee/bus/controller/BusStatsAnalyseController.java
  3. 0 23
      coffee-system/src/main/java/com/coffee/bus/controller/BusStatsReportController.java
  4. 8 6
      coffee-system/src/main/java/com/coffee/bus/entity/BusEvaluationEntity.java
  5. 6 12
      coffee-system/src/main/java/com/coffee/bus/enums/StatsAnalyseEnum.java
  6. 19 11
      coffee-system/src/main/java/com/coffee/bus/hospital/config/HospitalFinishMonitorConfigHandler.java
  7. 3 1
      coffee-system/src/main/java/com/coffee/bus/hospital/config/HospitalFunctionExtraConfigHandler.java
  8. 84 0
      coffee-system/src/main/java/com/coffee/bus/hospital/config/bean/FunctionEvalConfig.java
  9. 10 0
      coffee-system/src/main/java/com/coffee/bus/mapper/BusInfusionHistoryMapper.java
  10. 1 1
      coffee-system/src/main/java/com/coffee/bus/service/LocalBusHospitalConfigService.java
  11. 4 0
      coffee-system/src/main/java/com/coffee/bus/service/LocalBusInfusionHistoryService.java
  12. 1 1
      coffee-system/src/main/java/com/coffee/bus/service/LocalBusPatientService.java
  13. 90 0
      coffee-system/src/main/java/com/coffee/bus/service/dto/CombineEvalResult.java
  14. 119 1
      coffee-system/src/main/java/com/coffee/bus/stats/CommonStats.java
  15. 8 13
      coffee-system/src/main/java/com/coffee/bus/stats/analyse/AlarmStatsAnalyse.java
  16. 9 10
      coffee-system/src/main/java/com/coffee/bus/stats/analyse/AnalStatsAnalyse.java
  17. 541 1
      coffee-system/src/main/java/com/coffee/bus/stats/analyse/EvalStatsAnalyse.java
  18. 38 125
      coffee-system/src/main/java/com/coffee/bus/stats/analyse/InfusionDoseStatsAnalyse.java
  19. 14 35
      coffee-system/src/main/java/com/coffee/bus/stats/analyse/PcaStatsAnalyse.java
  20. 272 0
      coffee-system/src/main/java/com/coffee/bus/stats/entity/EvalTableResult.java
  21. 27 4
      coffee-system/src/main/java/com/coffee/bus/stats/entity/LineResult.java
  22. 5 1
      coffee-system/src/main/java/com/coffee/bus/stats/entity/StatsAnalyseResult.java
  23. 1 1
      coffee-system/src/main/java/com/coffee/bus/stats/entity/StatsColumn.java
  24. 7 2
      coffee-system/src/main/java/com/coffee/bus/stats/entity/TableResult.java
  25. 43 0
      coffee-system/src/main/java/com/coffee/bus/stats/enums/AgeStatsEnum.java
  26. 37 0
      coffee-system/src/main/java/com/coffee/bus/stats/enums/WeightRangeEnum.java
  27. 181 0
      coffee-system/src/main/java/com/coffee/bus/stats/report/AgeAndGenderStatsReport.java
  28. 170 0
      coffee-system/src/main/java/com/coffee/bus/stats/report/AgeStatsReport.java
  29. 183 0
      coffee-system/src/main/java/com/coffee/bus/stats/report/AsaStatsReport.java
  30. 150 8
      coffee-system/src/main/java/com/coffee/bus/stats/report/GenderStatsReport.java
  31. 176 0
      coffee-system/src/main/java/com/coffee/bus/stats/report/WeightStatsReport.java
  32. 13 3
      coffee-system/src/main/java/com/coffee/bus/web/handler/CheckTimeHandler.java
  33. 7 7
      coffee-system/src/main/java/com/coffee/bus/websocket/listener/DeviceInfoListener.java
  34. 188 0
      coffee-system/src/main/resources/mapper/bus/BusInfusionHistoryMapper.xml

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

@@ -94,10 +94,16 @@ public enum ResultCode implements IResultCode {
      */
     TOKEN_ERROR(HttpServletResponse.SC_REQUEST_TIMEOUT, "登录超时,请重新登录"),
 
+
     /**
      * 登录超时,请重新登录
      */
     TENANT_NONE(5001, "请选择医院"),
+
+    /**
+     * 请求过期
+     */
+    REQUEST_EXPIRE(5101, "请求过期"),
     ;
 
     /**

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

@@ -65,7 +65,7 @@ public class BusStatsAnalyseController {
             StatsAnalyseResult of = StatsAnalyseResult.of(commonStats.handlePie(result), commonStats.handleLine(result, query.getTimeUnit()), commonStats.handleTable(result,query.getTimeUnit()));
             return R.success(of);
         }
-        return R.success();
+        return R.success(StatsAnalyseResult.empty());
 
 
     }

+ 0 - 23
coffee-system/src/main/java/com/coffee/bus/controller/BusStatsReportController.java

@@ -1,23 +0,0 @@
-package com.coffee.bus.controller;
-
-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;
-
-/**
- * @author lifang
- * @version 1.0.0
- * @ClassName BusAnalyseController.java
- * @Description TODO
- * @createTime 2022年04月08日 10:24:00
- */
-@RestController
-@AllArgsConstructor
-@RequestMapping("/stats/report")
-@Api(tags = "报表分析",description = "统一权限前缀(stats:report),例如新增stats:report:add")
-public class BusStatsReportController {
-
-}

+ 8 - 6
coffee-system/src/main/java/com/coffee/bus/entity/BusEvaluationEntity.java

@@ -1,5 +1,7 @@
 package com.coffee.bus.entity;
 import com.baomidou.mybatisplus.annotation.TableName;
+
+import java.math.BigDecimal;
 import java.util.Date;
 import com.coffee.common.entity.TenantGenericEntity;
 import io.swagger.annotations.ApiModel;
@@ -105,21 +107,21 @@ public class BusEvaluationEntity extends TenantGenericEntity<String,String> {
     private String evaluator;
 
     @ApiModelProperty(value = "收缩压")
-    private String  shrinkPressure;
+    private BigDecimal  shrinkPressure;
 
 
     @ApiModelProperty(value = "舒张压")
-    private String  diastolicPressure;
+    private BigDecimal  diastolicPressure;
 
     @ApiModelProperty(value = "心率")
-    private String heartRate;
+    private BigDecimal heartRate;
 
     @ApiModelProperty(value = "胎心")
-    private String fetalHeartRate;
+    private BigDecimal fetalHeartRate;
 
     @ApiModelProperty(value = "呼吸频率")
-    private String breathRate;
+    private BigDecimal breathRate;
 
     @ApiModelProperty(value = "血氧饱和度")
-    private String bloodOxygenSaturation;
+    private BigDecimal bloodOxygenSaturation;
 }

+ 6 - 12
coffee-system/src/main/java/com/coffee/bus/enums/StatsAnalyseEnum.java

@@ -31,18 +31,12 @@ public enum  StatsAnalyseEnum implements IEnum<Integer> {
     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;
+        StatsAnalyseEnum[] values = StatsAnalyseEnum.values();
+        for (StatsAnalyseEnum statsAnalyseEnum : values) {
+            if(value.equals(statsAnalyseEnum.getValue())){
+                return statsAnalyseEnum;
+            }
         }
+        return null;
     }
 }

+ 19 - 11
coffee-system/src/main/java/com/coffee/bus/hospital/config/HospitalFinishMonitorConfigHandler.java

@@ -1,6 +1,7 @@
 package com.coffee.bus.hospital.config;
 
 
+import com.coffee.bus.entity.BusInfusionHistoryEntity;
 import com.coffee.bus.entity.BusPatientEntity;
 import com.coffee.bus.hospital.config.bean.FunctionFinishMonitorConfig;
 import com.coffee.bus.entity.BusDeviceRunningEntity;
@@ -72,7 +73,7 @@ public class HospitalFinishMonitorConfigHandler extends AbstractHospitalConfigHa
         }
     }
 
-    public void judgeNoSignalAutoFinish(String device, String patientCode, String tenantId, String infusionId){
+    public void judgeNoSignalAutoFinish(String device, String patientCode, String tenantId, String infusionId, Date uploadTime){
         FunctionFinishMonitorConfig config = this.getConfig().as(FunctionFinishMonitorConfig.class);
         if(config==null||!Boolean.TRUE.equals(config.isEnable())|| Objects.isNull(config.getNoSignalInterval())){
             return;
@@ -90,6 +91,7 @@ public class HospitalFinishMonitorConfigHandler extends AbstractHospitalConfigHa
                 .config(undoDeviceConfig)
                 .infusionId(infusionId)
                 .timeout(config.getNoSignalInterval())
+                .uploadTime(uploadTime)
                 //todo
                 .unit(TimeUnit.MINUTES)
                 .tenantId(tenantId)
@@ -125,6 +127,7 @@ public class HospitalFinishMonitorConfigHandler extends AbstractHospitalConfigHa
                     .patientCode(source.getPatientCode())
                     .config(undoDeviceConfig)
                     .timeout(config.getShutDownInterval())
+                    .uploadTime(source.getUploadTime())
                     //todo
                     .unit(TimeUnit.MINUTES)
                     .tenantId(source.getTenantId())
@@ -147,16 +150,20 @@ public class HospitalFinishMonitorConfigHandler extends AbstractHospitalConfigHa
         String patientCode = source.getPatientCode();
         BusPatientEntity patient = patientService.findByCode(patientCode, source.getTenantId());
         if (source.getInfusionId().equals(patient.getInfusionId())) {
-            UndoDeviceConfig config = source.getConfig();
-            ManualUndoConfig manualUndoConfig = new ManualUndoConfig();
-            config.setUndoTime(new Date());
-            manualUndoConfig.setTenantId(source.getTenantId());
-            manualUndoConfig.setUndo(config);
-            manualUndoConfig.setMonitorType(true);
-            manualUndoConfig.setClinicId(patient.getClinicId());
-            manualUndoConfig.setPatientCode(patientCode);
-            manualUndoConfig.setInfusionIds(Collections.singletonList(source.getInfusionId()));
-            infusionHistoryService.undo(manualUndoConfig,true);
+            BusInfusionHistoryEntity infusionHistory = infusionHistoryService.getById(patient.getInfusionId());
+            Date uploadTime = source.getUploadTime();
+            if (infusionHistory.getLastUploadTime().equals(uploadTime)) {
+                UndoDeviceConfig config = source.getConfig();
+                ManualUndoConfig manualUndoConfig = new ManualUndoConfig();
+                config.setUndoTime(new Date());
+                manualUndoConfig.setTenantId(source.getTenantId());
+                manualUndoConfig.setUndo(config);
+                manualUndoConfig.setMonitorType(true);
+                manualUndoConfig.setClinicId(patient.getClinicId());
+                manualUndoConfig.setPatientCode(patientCode);
+                manualUndoConfig.setInfusionIds(Collections.singletonList(source.getInfusionId()));
+                infusionHistoryService.undo(manualUndoConfig,true);
+            }
         }
     }
 
@@ -171,6 +178,7 @@ public class HospitalFinishMonitorConfigHandler extends AbstractHospitalConfigHa
         private Integer timeout;
         private TimeUnit unit;
         private String tenantId;
+        private Date uploadTime;
         private Date timestamp;
     }
 }

+ 3 - 1
coffee-system/src/main/java/com/coffee/bus/hospital/config/HospitalFunctionExtraConfigHandler.java

@@ -113,6 +113,7 @@ public class HospitalFunctionExtraConfigHandler  extends  AbstractHospitalConfig
                 .timestamp(new Date())
                 .historyId(source.getHistoryId())
                 .infusionId(source.getInfusionId())
+                .uploadTime(source.getUploadTime())
                 .timeout(interval)
                 .unit(TimeUnit.MINUTES)
                 .build();
@@ -151,7 +152,7 @@ public class HospitalFunctionExtraConfigHandler  extends  AbstractHospitalConfig
                     .setStatus(DeviceStatusEnum.NoSignal);
             wsPublishUtils.publishPatientMonitor(infusionHistory.getPatientCode(), infusionHistory.getTenantId());
             //不在服务区
-            monitorConfigHandler.judgeNoSignalAutoFinish(deviceId,source.getPatientCode(),source.getTenantId(),source.getInfusionId());
+            monitorConfigHandler.judgeNoSignalAutoFinish(deviceId,source.getPatientCode(),source.getTenantId(),source.getInfusionId(),source.getUploadTime());
             autoUndoConfigHandler.judgeNoSignalAutoUndo(deviceId,source.getPatientCode(),source.getTenantId(),source.getInfusionId());
         }
     }
@@ -232,6 +233,7 @@ public class HospitalFunctionExtraConfigHandler  extends  AbstractHospitalConfig
         private String tenantId;
         private String infusionId;
         private String historyId;
+        private Date uploadTime;
         private Date timestamp;
     }
 

+ 84 - 0
coffee-system/src/main/java/com/coffee/bus/hospital/config/bean/FunctionEvalConfig.java

@@ -0,0 +1,84 @@
+package com.coffee.bus.hospital.config.bean;
+
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName FunctionEvalConfig.java
+ * @Description TODO
+ * @createTime 2022年06月08日 21:11:00
+ */
+@Data
+public class FunctionEvalConfig {
+    @ApiModelProperty(value = "静息疼痛")
+    private Boolean statics;
+
+    @ApiModelProperty(value = "活动疼痛")
+    private Boolean activity;
+
+    @ApiModelProperty(value = "镇静评分")
+    private Boolean calm;
+
+    @ApiModelProperty(value = "左上肢")
+    private Boolean leftArm;
+
+    @ApiModelProperty(value = "左下肢")
+    private Boolean leftLeg;
+
+    @ApiModelProperty(value = "右上肢")
+    private Boolean rightArm;
+
+    @ApiModelProperty(value = "右下肢")
+    private Boolean rightLeg;
+
+    @ApiModelProperty(value = "恶心呕吐")
+    private Boolean nauseaVomit;
+
+    @ApiModelProperty(value = "瘙痒")
+    private Boolean itch;
+
+    @ApiModelProperty(value = "眩晕")
+    private Boolean vertigo;
+
+    @ApiModelProperty(value = "咽喉疼痛")
+    private Boolean soreThroat;
+
+    @ApiModelProperty(value = "尿潴留")
+    private Boolean uroschesis;
+
+    @ApiModelProperty(value = "呼吸抑制")
+    private Boolean breathDepression;
+
+    @ApiModelProperty(value = "声音嘶哑")
+    private Boolean hoarseness;
+
+    @ApiModelProperty(value = "认知障碍")
+    private Boolean cognitionObstacle;
+
+    @ApiModelProperty(value = "满意度")
+    private Boolean satisfaction;
+
+
+    @ApiModelProperty(value = "收缩压")
+    private Boolean shrinkPressure;
+
+
+    @ApiModelProperty(value = "舒张压")
+    private Boolean  diastolicPressure;
+
+    @ApiModelProperty(value = "心率")
+    private Boolean heartRate;
+
+    @ApiModelProperty(value = "胎心")
+    private Boolean fetalHeartRate;
+
+    @ApiModelProperty(value = "呼吸频率")
+    private Boolean breathRate;
+
+    @ApiModelProperty(value = "血氧饱和度")
+    private Boolean bloodOxygenSaturation;
+}

+ 10 - 0
coffee-system/src/main/java/com/coffee/bus/mapper/BusInfusionHistoryMapper.java

@@ -5,6 +5,7 @@ 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.CombineEvalResult;
 import com.coffee.bus.service.dto.CombineQuery;
 import com.coffee.bus.service.dto.CombineResult;
 import org.apache.ibatis.annotations.Mapper;
@@ -58,4 +59,13 @@ public interface BusInfusionHistoryMapper extends BaseMapper<BusInfusionHistoryE
      * @return List<CombineAlarmResult>
      */
     List<CombineResult> queryStatsAnal(@Param("query") CombineQuery query);
+
+    /**
+     * 描述: 联合查询出输注评分信息-统计使用
+     * @author lifang
+     * @date 2022/6/7 10:03
+     * @param query
+     * @return List<CombineAlarmResult>
+     */
+    List<CombineEvalResult> queryStatsEval(@Param("query") CombineQuery query);
 }

+ 1 - 1
coffee-system/src/main/java/com/coffee/bus/service/LocalBusHospitalConfigService.java

@@ -106,7 +106,7 @@ public class LocalBusHospitalConfigService extends BaseService<BusHospitalConfig
     public BusHospitalConfigEntity getDefaultConfig( ConfigEnum type){
         return this.getOne(new QueryWrapper<BusHospitalConfigEntity>()
                 .lambda()
-                .eq(BusHospitalConfigEntity::getType,type)
+                .eq(BusHospitalConfigEntity::getType,type.getValue())
                 .eq(BusHospitalConfigEntity::getTenantId, "1"));
     }
 

+ 4 - 0
coffee-system/src/main/java/com/coffee/bus/service/LocalBusInfusionHistoryService.java

@@ -370,4 +370,8 @@ public class LocalBusInfusionHistoryService extends BaseService<BusInfusionHisto
     public List<CombineAlarmResult> queryStatsAlarm(CombineQuery query) {
         return this.baseMapper.queryStatsAlarm(query);
     }
+
+    public List<CombineEvalResult> queryStatsEval(CombineQuery query) {
+        return this.baseMapper.queryStatsEval(query);
+    }
 }

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

@@ -217,7 +217,7 @@ public class LocalBusPatientService extends BaseService<BusPatientMapper, BusPat
     @Transactional(rollbackFor = Exception.class)
     public void manualEdit(BusClinicEntity clinic) {
         //先更新手术信息 todo
-        clinicService.saveOrUpdate(clinic);
+        clinicService.updateById(clinic);
         //后更新病人信息
         BusPatientEntity patient = BusPatientEntity.of(clinic);
         PatientOperator patientOperator = patientRegistry.getOperator(patient.getTenantId(), patient.getCode());

+ 90 - 0
coffee-system/src/main/java/com/coffee/bus/service/dto/CombineEvalResult.java

@@ -0,0 +1,90 @@
+package com.coffee.bus.service.dto;
+
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.hibernate.validator.constraints.Length;
+
+import java.math.BigDecimal;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName CombineEvalResult.java
+ * @Description TODO
+ * @createTime 2022年06月07日 09:50:00
+ */
+@Data
+public class CombineEvalResult extends CombineResult {
+
+    @ApiModelProperty(value = "评价id")
+    private String evalId;
+
+    @ApiModelProperty(value = "静息疼痛")
+    private Integer statics;
+
+    @ApiModelProperty(value = "活动疼痛")
+    private Integer activity;
+
+    @ApiModelProperty(value = "镇静评分")
+    private Integer calm;
+
+    @ApiModelProperty(value = "左上肢")
+    private Integer leftArm;
+
+    @ApiModelProperty(value = "左下肢")
+    private Integer leftLeg;
+
+    @ApiModelProperty(value = "右上肢")
+    private Integer rightArm;
+
+    @ApiModelProperty(value = "右下肢")
+    private Integer rightLeg;
+
+    @ApiModelProperty(value = "恶心呕吐")
+    private Integer nauseaVomit;
+
+    @ApiModelProperty(value = "瘙痒")
+    private Integer itch;
+
+    @ApiModelProperty(value = "眩晕")
+    private Integer vertigo;
+
+    @ApiModelProperty(value = "咽喉疼痛")
+    private Integer soreThroat;
+
+    @ApiModelProperty(value = "尿潴留")
+    private Integer uroschesis;
+
+    @ApiModelProperty(value = "呼吸抑制")
+    private Integer breathDepression;
+
+    @ApiModelProperty(value = "声音嘶哑")
+    private Integer hoarseness;
+
+    @ApiModelProperty(value = "认知障碍")
+    private Integer cognitionObstacle;
+
+    @ApiModelProperty(value = "满意度")
+    private Integer satisfaction;
+
+
+    @ApiModelProperty(value = "收缩压")
+    private BigDecimal shrinkPressure;
+
+
+    @ApiModelProperty(value = "舒张压")
+    private BigDecimal  diastolicPressure;
+
+    @ApiModelProperty(value = "心率")
+    private BigDecimal heartRate;
+
+    @ApiModelProperty(value = "胎心")
+    private BigDecimal fetalHeartRate;
+
+    @ApiModelProperty(value = "呼吸频率")
+    private BigDecimal breathRate;
+
+    @ApiModelProperty(value = "血氧饱和度")
+    private BigDecimal bloodOxygenSaturation;
+}

+ 119 - 1
coffee-system/src/main/java/com/coffee/bus/stats/CommonStats.java

@@ -13,6 +13,12 @@ import com.coffee.bus.stats.entity.TableResult;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
 /**
  * @author lifang
  * @version 1.0.0
@@ -57,7 +63,7 @@ public interface CommonStats<T extends CombineResult> {
      * @param results
      * @return TableResult
      */
-    TableResult handleTable(List<T> results, StatsTimeUnit unit);
+    List<TableResult> handleTable(List<T> results, StatsTimeUnit unit);
 
 
 
@@ -101,4 +107,116 @@ public interface CommonStats<T extends CombineResult> {
 
         return  BigDecimal.valueOf(Math.sqrt(subTotal.divide(BigDecimal.valueOf(CollectionUtil.size(sources)),2,RoundingMode.HALF_UP).doubleValue())).abs().setScale(2,BigDecimal.ROUND_HALF_UP);
     }
+
+
+    /**
+     * 描述:  创建混合折线图、即均值、最大值、最小值、方差
+     * @author lifang
+     * @date 2022/6/9 10:18
+     * @param groupByTime 按照时间划分
+     * @param result 折线图
+     * @param predicate 判定条件
+     * @param supply 转换函数
+     * @return LineResult<BigDecimal>
+     */
+    default   LineResult<BigDecimal> handleMixPolyLines(Map<String, List<T>> groupByTime,
+                                                        LineResult<BigDecimal> result,
+                                                        Consumer<T> peek,
+                                                        Predicate<T> predicate,
+                                                        Function<T,BigDecimal> supply){
+        List<String> time = result.getTime();
+        List<BigDecimal> totalData=new ArrayList<>();
+        //根据时间区间对镇痛方式进行统计
+        LineResult.LineContent<BigDecimal> lineContent = new LineResult.LineContent<BigDecimal>("均数");
+        groupByTime.forEach((timeRange,combineResults)->{
+            List<BigDecimal> sources = combineResults.stream()
+                    .peek(peek)
+                    .filter(predicate)
+                    .map(supply)
+                    .collect(Collectors.toList());
+            addMixPolyLinesData(sources,lineContent);
+            totalData.addAll(sources);
+            time.add(timeRange);
+        });
+
+        BigDecimal max=BigDecimal.ZERO;
+        BigDecimal min=BigDecimal.valueOf(Integer.MAX_VALUE);
+        BigDecimal total=BigDecimal.ZERO;
+        for (BigDecimal source : totalData) {
+            if(source!=null){
+                max=source.compareTo(max)>0?source:max;
+                min=source.compareTo(min)<0?source:min;
+                total=total.add(source);
+            }
+        }
+        BigDecimal average = CollectionUtil.isEmpty(totalData)?BigDecimal.ZERO:total.divide(BigDecimal.valueOf(CollectionUtil.size(totalData)), 2, RoundingMode.HALF_UP);
+
+        result.setSampleCount(CollectionUtil.size(totalData));
+        result.setAverage(average);
+        result.setVariance(computeStandardDeviation(totalData, average));
+        result.setMax(max);
+        result.setMin(min.compareTo(BigDecimal.valueOf(Integer.MAX_VALUE))==0?BigDecimal.ZERO:min);
+        result.setContent(Arrays.asList(lineContent));
+        return result;
+    }
+
+    /**
+     * 描述:  创建混合折线图、即均值、最大值、最小值、方差
+     * @author lifang
+     * @date 2022/6/9 10:18
+     * @param groupByTime 按照时间划分
+     * @param result 折线图
+     * @param predicate 判定条件
+     * @param supply 转换函数
+     * @return LineResult<BigDecimal>
+     */
+    default   LineResult<BigDecimal> handleMixPolyLines(Map<String, List<T>> groupByTime,
+                                                        LineResult<BigDecimal> result,
+                                                        Predicate<T> predicate,
+                                                        Function<T,BigDecimal> supply){
+        return handleMixPolyLines(groupByTime,result, ignore->{},predicate,supply);
+    }
+
+    /**
+     * 描述: 为线段添加最大值、最小值、方差、样本总数
+     * @author lifang
+     * @date 2022/6/9 10:14
+     * @param sources
+     * @param lineContent
+     * @return void
+     */
+    default void addMixPolyLinesData(List<BigDecimal> sources,LineResult.LineContent<BigDecimal> lineContent){
+        BigDecimal max=BigDecimal.ZERO;
+        BigDecimal min=BigDecimal.valueOf(Integer.MAX_VALUE);
+        BigDecimal total=BigDecimal.ZERO;
+        ArrayList<BigDecimal> allDatas = 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);
+                allDatas.add(source);
+            }
+        }
+        List<BigDecimal> maxList = Optional.ofNullable(lineContent.getMax()).orElse(new ArrayList<>());
+        List<BigDecimal> minList = Optional.ofNullable(lineContent.getMin()).orElse(new ArrayList<>());
+        List<BigDecimal> averageList = Optional.ofNullable(lineContent.getAverage()).orElse(new ArrayList<>());
+        List<List<BigDecimal>> varianceList = Optional.ofNullable(lineContent.getVariance()).orElse(new ArrayList<>());
+        List<Integer> sampleCount = Optional.ofNullable(lineContent.getSampleCount()).orElse(new ArrayList<>());
+
+        BigDecimal average = CollectionUtil.isEmpty(sources)?BigDecimal.ZERO:total.divide(BigDecimal.valueOf(CollectionUtil.size(sources)), 2, RoundingMode.HALF_UP);
+        BigDecimal variance = computeStandardDeviation(allDatas, average);
+        varianceList.add(Arrays.asList(average.subtract(variance),average.add(variance)));
+        averageList.add(average);
+        sampleCount.add(CollectionUtil.size(sources));
+        maxList.add(max);
+        minList.add(min.compareTo(BigDecimal.valueOf(Integer.MAX_VALUE))==0?BigDecimal.ZERO:min);
+
+
+        lineContent.setMax(maxList);
+        lineContent.setMin(minList);
+        lineContent.setAverage(averageList);
+        lineContent.setVariance(varianceList);
+        lineContent.setSampleCount(sampleCount);
+    }
 }

+ 8 - 13
coffee-system/src/main/java/com/coffee/bus/stats/analyse/AlarmStatsAnalyse.java

@@ -2,7 +2,6 @@ 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;
@@ -57,6 +56,7 @@ public class AlarmStatsAnalyse implements CommonStats<CombineAlarmResult> {
         List<LineResult> result = new ArrayList<>();
         result.add(countLine(groupByTime));
         result.add(ratioLine(groupByTime));
+
         //比率
         return result;
     }
@@ -69,11 +69,8 @@ public class AlarmStatsAnalyse implements CommonStats<CombineAlarmResult> {
      * @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("此数据为 报警率 【次数统计】");
+        LineResult<BigDecimal> result =LineResult.of("ratio",true,false,"报警率分布图");
+        List<String> time = result.getTime();
         Map<String, AlarmTotalPieResult> groupByTime = new HashMap<>();
         //根据时间区间对镇痛方式进行统计
         results.forEach((timeRange,combineResults)->{
@@ -118,11 +115,8 @@ public class AlarmStatsAnalyse implements CommonStats<CombineAlarmResult> {
      * @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("此数据为 报警方式 【次数统计】");
+        LineResult<Long> result = LineResult.of("count",false,false,"报警次数分布图");
+        List<String> time = result.getTime();
         Map<String, AlarmTotalPieResult> groupByTime = new HashMap<>();
         //根据时间区间对镇痛方式进行统计
         results.forEach((timeRange,combineResults)->{
@@ -145,13 +139,14 @@ public class AlarmStatsAnalyse implements CommonStats<CombineAlarmResult> {
             lineContentMap.computeIfAbsent("低输注",k->new LineResult.LineContent<>("低输注")).getValue().add(totalResult.getLowInfusion().getValue());
         });
         result.setContent(new ArrayList<>(lineContentMap.values()));
+        result.setSampleCount(CollectionUtil.size(results));
         return result;
     }
 
 
 
     @Override
-    public TableResult handleTable(List<CombineAlarmResult> results, StatsTimeUnit unit) {
+    public  List<TableResult> handleTable(List<CombineAlarmResult> results, StatsTimeUnit unit) {
         TableResult result = new TableResult();
         //根据时间对结果进行区分统计
         Map<String, List<CombineAlarmResult>> groupByTimeResults = groupByTime(results, unit);
@@ -201,7 +196,7 @@ public class AlarmStatsAnalyse implements CommonStats<CombineAlarmResult> {
 
             contents.add(contentValues);
         });
-        return result;
+        return Arrays.asList(result);
     }
 
 

+ 9 - 10
coffee-system/src/main/java/com/coffee/bus/stats/analyse/AnalStatsAnalyse.java

@@ -71,7 +71,9 @@ public class AnalStatsAnalyse implements CommonStats<CombineResult> {
         //获取所有镇痛信息
         List<String> allAnal = getAllAnalName( groupByAnal(results), getConAnal());
         List<LineResult> result = new ArrayList<>();
-        result.add(countLine(groupByTime,allAnal));
+        LineResult<Long> countLineResult = countLine(groupByTime, allAnal);
+        countLineResult.setSampleCount(CollectionUtil.size(results));
+        result.add(countLineResult);
         result.add(ratioLine(groupByTime,allAnal));
         //比率
         return result;
@@ -87,7 +89,7 @@ public class AnalStatsAnalyse implements CommonStats<CombineResult> {
      * @return TableResult
      */
     @Override
-    public TableResult handleTable(List<CombineResult> results, StatsTimeUnit unit) {
+    public  List<TableResult> handleTable(List<CombineResult> results, StatsTimeUnit unit) {
         TableResult result = new TableResult();
         //根据时间对结果进行区分统计
         Map<String, LineResult.LineContent<Long>> content = new HashMap<>();
@@ -128,7 +130,7 @@ public class AnalStatsAnalyse implements CommonStats<CombineResult> {
             contents.add(contentValues);
         });
 
-        return result;
+        return Arrays.asList(result);
     }
 
 
@@ -182,12 +184,9 @@ public class AnalStatsAnalyse implements CommonStats<CombineResult> {
      * @return LineResult<Long>
      */
     private LineResult<Long> countLine( Map<String, List<CombineResult>> groupByTime, List<String> allAnal){
-        LineResult<Long> result = new LineResult<>();
+        LineResult<Long> result = LineResult.of("count",false,false,"镇痛方式数量分布图");
         Map<String, LineResult.LineContent<Long>> content = new HashMap<>();
-        List<String> time = new ArrayList<>();
-        result.setTime(time);
-        result.setId("count");
-        result.setDescription("此数据为 镇痛方式 【次数统计】");
+        List<String> time =result.getTime();
 
         //根据时间区间对镇痛方式进行统计
         groupByTime.forEach((timeRange,combineResult)->{
@@ -221,8 +220,8 @@ public class AnalStatsAnalyse implements CommonStats<CombineResult> {
         List<String> time = new ArrayList<>();
         result.setTime(time);
         result.setId("ratio");
-        result.setDescription("此数据为 镇痛方式 【比率统计】");
-
+        result.setDescription("镇痛方式占比分布图");
+        result.setRatio(true);
         //根据时间区间对镇痛方式进行统计
         groupByTime.forEach((timeRange,combineResult)->{
             //已有的镇痛方式数量

+ 541 - 1
coffee-system/src/main/java/com/coffee/bus/stats/analyse/EvalStatsAnalyse.java

@@ -1,5 +1,29 @@
 package com.coffee.bus.stats.analyse;
 
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.coffee.bus.entity.BusHospitalConfigEntity;
+import com.coffee.bus.enums.ConfigEnum;
+import com.coffee.bus.enums.StatsAnalyseEnum;
+import com.coffee.bus.enums.StatsTimeUnit;
+import com.coffee.bus.hospital.config.bean.FunctionEvalConfig;
+import com.coffee.bus.service.LocalBusHospitalConfigService;
+import com.coffee.bus.service.LocalBusInfusionHistoryService;
+import com.coffee.bus.service.dto.CombineEvalResult;
+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
@@ -7,5 +31,521 @@ package com.coffee.bus.stats.analyse;
  * @Description TODO
  * @createTime 2022年06月07日 21:10:00
  */
-public class EvalStatsAnalyse {
+@Data
+@AllArgsConstructor
+@Service
+public class EvalStatsAnalyse implements CommonStats<CombineEvalResult> {
+    private final LocalBusInfusionHistoryService infusionHistoryService;
+    private final LocalBusHospitalConfigService configService;
+    @Override
+    public List<CombineEvalResult> queryResult(CombineQuery query) {
+        return infusionHistoryService.queryStatsEval(query);
+    }
+
+    @Override
+    public StatsAnalyseEnum getId() {
+        return StatsAnalyseEnum.eval;
+    }
+
+    @Override
+    public List<PieResult> handlePie(List<CombineEvalResult> results) {
+        PieResult result = PieResult.of(PieEnum.total.name(),PieEnum.total.getText());
+        //keys由历史数据和常量维护信息表并集构成
+        BusHospitalConfigEntity config = evalConfig();
+        if(config==null){
+            return Arrays.asList(result);
+        }
+        FunctionEvalConfig evalConfig = JSONUtil.toBean(JSONUtil.toJsonStr(config.getConfig()), FunctionEvalConfig.class);
+        Map<String, Long> resultMap = new HashMap<>();
+        addPieContent(evalConfig,resultMap);
+        computeEvalPie(results,resultMap);
+        resultMap.forEach(result::addContent);
+        return Arrays.asList(result);
+    }
+
+    private void computeEvalPie(List<CombineEvalResult> results, Map<String, Long> resultMap) {
+        for (CombineEvalResult result : results) {
+            if(null!=result.getStatics()){
+                resultMap.computeIfPresent("静息疼痛",(k,v)->v+1);
+            }
+            if(null!=result.getActivity()){
+                resultMap.computeIfPresent("活动疼痛",(k,v)->v+1);
+            }
+            if(null!=result.getCalm()){
+                resultMap.computeIfPresent("镇静评分",(k,v)->v+1);
+            }
+            if(null!=result.getLeftArm()){
+                resultMap.computeIfPresent("左上肢",(k,v)->v+1);
+            }
+            if(null!=result.getLeftLeg()){
+                resultMap.computeIfPresent("左下肢",(k,v)->v+1);
+            }
+            if(null!=result.getRightArm()){
+                resultMap.computeIfPresent("右上肢",(k,v)->v+1);
+            }
+            if(null!=result.getRightLeg()){
+                resultMap.computeIfPresent("右下肢",(k,v)->v+1);
+            }
+            if(null!=result.getNauseaVomit()){
+                resultMap.computeIfPresent("恶心呕吐",(k,v)->v+1);
+            }
+            if(null!=result.getItch()){
+                resultMap.computeIfPresent("瘙痒",(k,v)->v+1);
+            }
+            if(null!=result.getVertigo()){
+                resultMap.computeIfPresent("眩晕",(k,v)->v+1);
+            }
+            if(null!=result.getSoreThroat()){
+                resultMap.computeIfPresent("咽喉疼痛",(k,v)->v+1);
+            }
+            if(null!=result.getUroschesis()){
+                resultMap.computeIfPresent("尿潴留",(k,v)->v+1);
+            }
+            if(null!=result.getBreathDepression()){
+                resultMap.computeIfPresent("呼吸抑制",(k,v)->v+1);
+            }
+            if(null!=result.getHoarseness()){
+                resultMap.computeIfPresent("声音嘶哑",(k,v)->v+1);
+            }
+            if(null!=result.getCognitionObstacle()){
+                resultMap.computeIfPresent("认知障碍",(k,v)->v+1);
+            }
+            if(null!=result.getShrinkPressure()){
+                resultMap.computeIfPresent("收缩压",(k,v)->v+1);
+            }
+            if(null!=result.getDiastolicPressure()){
+                resultMap.computeIfPresent("舒张压",(k,v)->v+1);
+            }
+            if(null!=result.getHeartRate()){
+                resultMap.computeIfPresent("心率",(k,v)->v+1);
+            }
+            if(null!=result.getFetalHeartRate()){
+                resultMap.computeIfPresent("胎心",(k,v)->v+1);
+            }
+            if(null!=result.getBreathRate()){
+                resultMap.computeIfPresent("呼吸频率",(k,v)->v+1);
+            }
+            if(null!=result.getBloodOxygenSaturation()){
+                resultMap.computeIfPresent("血氧饱和度",(k,v)->v+1);
+            }
+        }
+    }
+
+    private void addPieContent(FunctionEvalConfig evalConfig, Map<String, Long> resultMap) {
+        if(Boolean.TRUE.equals(evalConfig.getStatics())){
+            resultMap.put("静息疼痛",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getActivity())){
+            resultMap.put("活动疼痛",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getCalm())){
+            resultMap.put("镇静评分",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getLeftArm())){
+            resultMap.put("左上肢",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getLeftLeg())){
+            resultMap.put("左下肢",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getRightArm())){
+            resultMap.put("右上肢",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getRightLeg())){
+            resultMap.put("右下肢",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getNauseaVomit())){
+            resultMap.put("恶心呕吐",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getItch())){
+            resultMap.put("瘙痒",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getVertigo())){
+            resultMap.put("眩晕",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getSoreThroat())){
+            resultMap.put("咽喉疼痛",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getUroschesis())){
+            resultMap.put("尿潴留",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getBreathDepression())){
+            resultMap.put("呼吸抑制",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getHoarseness())){
+            resultMap.put("声音嘶哑",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getCognitionObstacle())){
+            resultMap.put("认知障碍",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getShrinkPressure())){
+            resultMap.put("收缩压",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getDiastolicPressure())){
+            resultMap.put("舒张压",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getHeartRate())){
+            resultMap.put("心率",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getFetalHeartRate())){
+            resultMap.put("胎心",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getBreathRate())){
+            resultMap.put("呼吸频率",0L);
+        }
+        if(Boolean.TRUE.equals(evalConfig.getBloodOxygenSaturation())){
+            resultMap.put("血氧饱和度",0L);
+        }
+    }
+
+    @Override
+    public List<LineResult> handleLine(List<CombineEvalResult> results, StatsTimeUnit unit) {
+        //对所有结果进行时间划分
+        Map<String, List<CombineEvalResult>> groupByTime = groupByTime(results, unit);
+        //获取所有镇痛信息
+        List<LineResult> result = new ArrayList<>();
+        result.add(addRatio(groupByTime));
+        //只对包含评价的数据进行时间划分
+        Map<String, List<CombineEvalResult>> exitEvalGroupByTime = groupByTime(
+                results
+                        .stream()
+                        .filter(evalResult -> StrUtil.isNotEmpty(evalResult.getEvalId()))
+                        .collect(Collectors.toList())
+                , unit);
+        BusHospitalConfigEntity config = evalConfig();
+        if(config==null){
+            return result;
+        }
+        FunctionEvalConfig evalConfig = JSONUtil.toBean(JSONUtil.toJsonStr(config.getConfig()), FunctionEvalConfig.class);
+
+        addEvalLine(exitEvalGroupByTime,result,evalConfig);
+        //比率
+        return result;
+    }
+
+    @Override
+    public List<TableResult> handleTable(List<CombineEvalResult> results, StatsTimeUnit unit) {
+        TableResult result = new TableResult();
+        BusHospitalConfigEntity config = evalConfig();
+        FunctionEvalConfig evalConfig = JSONUtil.toBean(JSONUtil.toJsonStr(config.getConfig()), FunctionEvalConfig.class);
+        //根据时间对结果进行区分统计
+        Map<String, List<CombineEvalResult>> groupByTime = groupByTime(results, unit);
+
+        List<StatsColumn> allColumn = getAllColumn(evalConfig);
+
+        List<Map<String, Object>> contents = new ArrayList<>();
+        result.setContent(contents);
+        result.setColumn(allColumn);
+        //根据时间区间对镇痛方式进行统计
+        groupByTime.forEach((timeRange,combineResult)->{
+            BigDecimal totalSize = BigDecimal.valueOf(CollectionUtil.size(combineResult));
+            //表格内容
+            long evalCount = combineResult.stream().filter(r -> StrUtil.isNotEmpty(r.getEvalId())).count();
+            LinkedHashMap<String, Object> contentValues = new LinkedHashMap<>();
+            contentValues.put("time",timeRange);
+            contentValues.put("infusionCount",totalSize);
+            contentValues.put("evalCount",evalCount);
+            contentValues.put("evalRatio",computeRatio(BigDecimal.valueOf(evalCount),totalSize));
+            //获取特定时间区间内
+            EvalTableResult evalTableResult = new EvalTableResult();
+            combineResult.stream()
+                    .filter(evalResult -> StrUtil.isNotEmpty(evalResult.getEvalId()))
+                    .forEach(evalTableResult::handle);
+            contentValues.putAll(evalTableResult);
+            contents.add(contentValues);
+        });
+
+        return Arrays.asList(result);
+    }
+
+    private List<StatsColumn> getAllColumn(FunctionEvalConfig config) {
+        List<StatsColumn> result = new ArrayList<>();
+        result.add(StatsColumn.of("时间","time"));
+        result.add(StatsColumn.of("输注次数","infusionCount"));
+        result.add(StatsColumn.of("评价次数","evalCount"));
+        result.add(StatsColumn.of("评价率","evalRatio",true));
+        if(Boolean.TRUE.equals(config.getStatics())){
+            result.add(StatsColumn.of("静息疼痛次数","静息疼痛次数"));
+        }
+        if(Boolean.TRUE.equals(config.getActivity())){
+            result.add(StatsColumn.of("活动疼痛次数","活动疼痛次数"));
+        }
+        if(Boolean.TRUE.equals(config.getCalm())){
+            result.add(StatsColumn.of("镇静评分次数","镇静评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getLeftArm())){
+            result.add(StatsColumn.of("左上肢评分次数","左上肢评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getLeftLeg())){
+            result.add(StatsColumn.of("左下肢评分次数","左下肢评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getRightArm())){
+            result.add(StatsColumn.of("右上肢评分次数","右上肢评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getRightLeg())){
+            result.add(StatsColumn.of("右下肢评分次数","右下肢评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getNauseaVomit())){
+            result.add(StatsColumn.of("恶心呕吐评分次数","恶心呕吐评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getItch())){
+            result.add(StatsColumn.of("瘙痒评分次数","瘙痒评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getVertigo())){
+            result.add(StatsColumn.of("眩晕评分次数","眩晕评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getSoreThroat())){
+            result.add(StatsColumn.of("咽喉疼痛评分次数","咽喉疼痛评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getUroschesis())){
+            result.add(StatsColumn.of("尿潴留评分次数","尿潴留评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getBreathDepression())){
+            result.add(StatsColumn.of("呼吸抑制评分次数","呼吸抑制评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getHoarseness())){
+            result.add(StatsColumn.of("声音嘶哑评分次数","声音嘶哑评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getCognitionObstacle())){
+            result.add(StatsColumn.of("认知障碍评分次数","认知障碍评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getShrinkPressure())){
+            result.add(StatsColumn.of("收缩压评分次数","收缩压评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getDiastolicPressure())){
+            result.add(StatsColumn.of("舒张压评分次数","舒张压评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getHeartRate())){
+            result.add(StatsColumn.of("心率评分次数","心率评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getFetalHeartRate())){
+            result.add(StatsColumn.of("胎心评分次数","胎心评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getBreathRate())){
+            result.add(StatsColumn.of("呼吸频率评分次数","呼吸频率评分次数"));
+        }
+        if(Boolean.TRUE.equals(config.getBloodOxygenSaturation())){
+            result.add(StatsColumn.of("血氧饱和度评分次数","血氧饱和度评分次数"));
+        }
+        return result;
+    }
+
+    /**
+     * 描述:添加评价比率
+     * @author lifang
+     * @date 2022/6/8 21:50
+     * @param groupByTime
+     * @return LineResult
+     */
+    private LineResult<BigDecimal> addRatio(Map<String, List<CombineEvalResult>> groupByTime) {
+        LineResult<BigDecimal> result = LineResult.of("ratio",true,false,"评价占比分布图");
+        LineResult.LineContent<BigDecimal> ratioLine =  new LineResult.LineContent<>("评价占比");
+        List<String> time = result.getTime();
+
+        //根据时间区间对镇痛方式进行统计
+        groupByTime.forEach((timeRange,combineResult)->{
+            BigDecimal totalSize = BigDecimal.valueOf(CollectionUtil.size(combineResult));
+            //获取特定时间区间内 评价次数
+            long evalCount = combineResult.stream().filter(r -> StrUtil.isNotEmpty(r.getEvalId())).count();
+            ratioLine.addValue(computeRatio(BigDecimal.valueOf(evalCount),totalSize));
+            time.add(timeRange);
+        });
+
+        result.setContent(Arrays.asList(ratioLine));
+        return result;
+    }
+
+    private void addEvalLine(Map<String, List<CombineEvalResult>> groupByTime ,List<LineResult> result, FunctionEvalConfig evalConfig) {
+        if (Boolean.TRUE.equals(evalConfig.getStatics())) {
+            LineResult<BigDecimal> staticsLine = LineResult.of("staticsLine",false,false,"静息疼痛评分走势图");
+
+            handleMixPolyLines(groupByTime,staticsLine,
+                    evalResult -> evalResult.getStatics()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getStatics()));
+            result.add(staticsLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getActivity())) {
+            LineResult<BigDecimal> activityLine =LineResult.of("activityLine",false,false,"活动疼痛评分走势图");
+            handleMixPolyLines(groupByTime,activityLine,
+                    evalResult -> evalResult.getActivity()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getActivity()));
+            result.add(activityLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getCalm())) {
+            LineResult<BigDecimal> clamLine = LineResult.of("clamLine",false,false,"镇静评分评分走势图");
+            handleMixPolyLines(groupByTime,clamLine,
+                    evalResult -> evalResult.getCalm()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getCalm()));
+            result.add(clamLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getLeftArm())) {
+            LineResult<BigDecimal> leftArmLine =  LineResult.of("leftArmLine",false,false,"左上肢肌力评分走势图");
+            handleMixPolyLines(groupByTime,leftArmLine,
+                    evalResult -> evalResult.getLeftArm()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getLeftArm()));
+            result.add(leftArmLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getRightArm())) {
+            LineResult<BigDecimal> rightArmLine = LineResult.of("rightArmLine",false,false,"右上肢肌力评分走势图");
+            handleMixPolyLines(groupByTime,rightArmLine,
+                    evalResult -> evalResult.getRightArm()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getRightArm()));
+            result.add(rightArmLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getLeftLeg())) {
+            LineResult<BigDecimal> leftLegLine =  LineResult.of("leftLegLine",false,false,"左下肢肌力评分走势图");
+            handleMixPolyLines(groupByTime,leftLegLine,
+                    evalResult -> evalResult.getLeftLeg()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getLeftLeg()));
+            result.add(leftLegLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getRightLeg())) {
+            LineResult<BigDecimal> rightLegLine =  LineResult.of("rightLegLine",false,false,"右下肢肌力评分走势图");
+            handleMixPolyLines(groupByTime,rightLegLine,
+                    evalResult -> evalResult.getRightLeg()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getRightLeg()));
+            result.add(rightLegLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getNauseaVomit())) {
+            LineResult<BigDecimal> nauseaVomitLine =LineResult.of("nauseaVomitLine",false,false,"恶心呕吐评分走势图");
+            handleMixPolyLines(groupByTime,nauseaVomitLine,
+                    evalResult -> evalResult.getNauseaVomit()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getNauseaVomit()));
+            result.add(nauseaVomitLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getItch())) {
+            LineResult<BigDecimal> itchLine = LineResult.of("itchLine",false,false,"瘙痒评分走势图");
+            handleMixPolyLines(groupByTime,itchLine,
+                    evalResult -> evalResult.getItch()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getItch()));
+            result.add(itchLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getVertigo())) {
+            LineResult<BigDecimal> vertigoLine = LineResult.of("vertigoLine",false,false,"眩晕评分走势图");
+            handleMixPolyLines(groupByTime,vertigoLine,
+                    evalResult -> evalResult.getVertigo()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getVertigo()));
+            result.add(vertigoLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getSoreThroat())) {
+            LineResult<BigDecimal> soreThroatLine = LineResult.of("soreThroatLine",false,false,"咽喉疼痛评分走势图");
+            handleMixPolyLines(groupByTime,soreThroatLine,
+                    evalResult -> evalResult.getSoreThroat()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getSoreThroat()));
+            result.add(soreThroatLine);
+        }
+
+
+        if (Boolean.TRUE.equals(evalConfig.getUroschesis())) {
+            LineResult<BigDecimal> uroschesisLine =  LineResult.of("uroschesisLine",false,false,"尿潴留评分走势图");
+            handleMixPolyLines(groupByTime,uroschesisLine,
+                    evalResult -> evalResult.getUroschesis()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getUroschesis()));
+            result.add(uroschesisLine);
+        }
+
+
+
+        if (Boolean.TRUE.equals(evalConfig.getBreathDepression())) {
+            LineResult<BigDecimal> breathDepressionLine =  LineResult.of("breathDepressionLine",false,false,"呼吸抑制评分走势图");
+            handleMixPolyLines(groupByTime,breathDepressionLine,
+                    evalResult -> evalResult.getBreathDepression()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getBreathDepression()));
+            result.add(breathDepressionLine);
+        }
+
+
+        if (Boolean.TRUE.equals(evalConfig.getHoarseness())) {
+            LineResult<BigDecimal> hoarsenessLine =  LineResult.of("hoarsenessLine",false,false,"眩晕评分走势图");
+            handleMixPolyLines(groupByTime,hoarsenessLine,
+                    evalResult -> evalResult.getHoarseness()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getHoarseness()));
+            result.add(hoarsenessLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getCognitionObstacle())) {
+            LineResult<BigDecimal> cognitionObstacleLine =  LineResult.of("cognitionObstacleLine",false,false,"认知障碍评分走势图");
+            handleMixPolyLines(groupByTime,cognitionObstacleLine,
+                    evalResult -> evalResult.getCognitionObstacle()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getCognitionObstacle()));
+            result.add(cognitionObstacleLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getSatisfaction())) {
+            LineResult<BigDecimal> satisfactionLine = LineResult.of("satisfactionLine",false,false,"满意度评分走势图");
+            handleMixPolyLines(groupByTime,satisfactionLine,
+                    evalResult -> evalResult.getSatisfaction()!=null,
+                    evalResult -> BigDecimal.valueOf(evalResult.getSatisfaction()));
+            result.add(satisfactionLine);
+        }
+
+
+        if (Boolean.TRUE.equals(evalConfig.getShrinkPressure())) {
+            LineResult<BigDecimal> shrinkPressureLine = LineResult.of("shrinkPressureLine",false,false,"收缩压走势图");
+            handleMixPolyLines(groupByTime,shrinkPressureLine,
+                    evalResult -> evalResult.getShrinkPressure()!=null,
+                    CombineEvalResult::getShrinkPressure);
+            result.add(shrinkPressureLine);
+        }
+
+
+        if (Boolean.TRUE.equals(evalConfig.getDiastolicPressure())) {
+            LineResult<BigDecimal> diastolicPressureLine =  LineResult.of("diastolicPressureLine",false,false,"舒张压走势图");
+            handleMixPolyLines(groupByTime,diastolicPressureLine,
+                    evalResult -> evalResult.getDiastolicPressure()!=null,
+                    CombineEvalResult::getDiastolicPressure);
+            result.add(diastolicPressureLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getHeartRate())) {
+            LineResult<BigDecimal> heartRateLine =LineResult.of("heartRateLine",false,false,"心率走势图");
+            handleMixPolyLines(groupByTime,heartRateLine,
+                    evalResult -> evalResult.getHeartRate()!=null,
+                    CombineEvalResult::getHeartRate);
+            result.add(heartRateLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getFetalHeartRate())) {
+            LineResult<BigDecimal> fetalHeartRateLine = LineResult.of("fetalHeartRateLine",false,false,"胎心走势图");
+            handleMixPolyLines(groupByTime,fetalHeartRateLine,
+                    evalResult -> evalResult.getFetalHeartRate()!=null,
+                    CombineEvalResult::getFetalHeartRate);
+            result.add(fetalHeartRateLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getBreathRate())) {
+            LineResult<BigDecimal> breathRateLine = LineResult.of("breathRateLine",false,false,"呼吸频率走势图");
+            handleMixPolyLines(groupByTime,breathRateLine,
+                    evalResult -> evalResult.getBreathRate()!=null,
+                    CombineEvalResult::getBreathRate);
+            result.add(breathRateLine);
+        }
+
+        if (Boolean.TRUE.equals(evalConfig.getBloodOxygenSaturation())) {
+            LineResult<BigDecimal> bloodOxygenSaturationLine = LineResult.of("bloodOxygenSaturationLine",false,false,"血氧饱和度走势图");
+            handleMixPolyLines(groupByTime,bloodOxygenSaturationLine,
+                    evalResult -> evalResult.getBloodOxygenSaturation()!=null,
+                    CombineEvalResult::getBloodOxygenSaturation);
+            result.add(bloodOxygenSaturationLine);
+        }
+
+
+    }
+
+
+
+
+
+    private BusHospitalConfigEntity evalConfig(){
+        return  configService.getOne(new QueryWrapper<BusHospitalConfigEntity>().lambda().eq(BusHospitalConfigEntity::getType, ConfigEnum.eval));
+    }
 }

+ 38 - 125
coffee-system/src/main/java/com/coffee/bus/stats/analyse/InfusionDoseStatsAnalyse.java

@@ -1,6 +1,5 @@
 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;
@@ -12,13 +11,8 @@ 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
@@ -69,59 +63,28 @@ public class InfusionDoseStatsAnalyse implements CommonStats<CombineResult> {
     }
 
     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));
+        LineResult<BigDecimal> result = LineResult.of("selfLockTime",false,false,"自控锁时分析走势图");
+        handleMixPolyLines(groupByTime,result,
+                combineResult -> {
+                    if (combineResult.getSelfControlLockTime() == null) {
+                        combineResult.setSelfControlLockTime(0);
+                    }
+                },
+                ignore->true,
+                combineResult -> BigDecimal.valueOf(combineResult.getSelfControlLockTime()));
         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));
+        LineResult<BigDecimal> result = LineResult.of("appendDoseLine",false,false,"追加量分析走势图");
+        handleMixPolyLines(groupByTime,result,
+                combineResult -> {
+                    if (combineResult.getAppendDose() == null) {
+                        combineResult.setAppendDose(BigDecimal.valueOf(0));
+                    }
+                },
+                ignore->true,
+                CombineResult::getAppendDose);
         return result;
     }
 
@@ -133,30 +96,15 @@ public class InfusionDoseStatsAnalyse implements CommonStats<CombineResult> {
      * @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));
+        LineResult<BigDecimal> result = LineResult.of("inputDoseLine",false,false,"已输入量分析走势图");
+        handleMixPolyLines(groupByTime,result,
+                combineResult -> {
+                    if (combineResult.getInputDose() == null) {
+                        combineResult.setInputDose(BigDecimal.valueOf(0));
+                    }
+                },
+                ignore->true,
+                CombineResult::getInputDose);
         return result;
     }
 
@@ -168,56 +116,21 @@ public class InfusionDoseStatsAnalyse implements CommonStats<CombineResult> {
      * @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));
+        LineResult<BigDecimal> result = LineResult.of("continueDoseLine",false,false,"持续量分析走势图");
+
+        handleMixPolyLines(groupByTime,result,
+                combineResult -> {
+                    if (combineResult.getContinueDose() == null) {
+                        combineResult.setContinueDose(BigDecimal.valueOf(0));
+                    }
+                },
+                ignore->true,
+                CombineResult::getContinueDose);
         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) {
+    public  List<TableResult> handleTable(List<CombineResult> results, StatsTimeUnit unit) {
         return null;
     }
 

+ 14 - 35
coffee-system/src/main/java/com/coffee/bus/stats/analyse/PcaStatsAnalyse.java

@@ -76,34 +76,16 @@ public class PcaStatsAnalyse implements CommonStats<CombineResult> {
      * @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));
+        LineResult<BigDecimal> result =LineResult.of("capital",false,false,"人均次数");
+        handleMixPolyLines(groupByTime,result,
+                combineResult -> {
+                    combineResult.setPcaTotalCount(
+                            Optional.ofNullable(combineResult.getPcaInvalidCount()).orElse(0)
+                            +
+                            Optional.ofNullable(combineResult.getPcaValidCount()).orElse(0));
+                },
+                ignore->true,
+                combineResult -> BigDecimal.valueOf(combineResult.getPcaTotalCount()));
         return result;
     }
 
@@ -115,13 +97,10 @@ public class PcaStatsAnalyse implements CommonStats<CombineResult> {
      * @return LineResult
      */
     private LineResult countLine(Map<String, List<CombineResult>> groupByTime) {
-        LineResult<Long> result = new LineResult<>();
+        LineResult<Long> result = LineResult.of("count",false,false,"自控次数分布图");
         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("此数据为 自控次数 【次数统计】");
+        List<String> time = result.getTime();
         //根据时间区间对pca次数进行统计
         groupByTime.forEach((timeRange,combineResults)->{
             long pcaValidTotalCount=0;
@@ -141,7 +120,7 @@ public class PcaStatsAnalyse implements CommonStats<CombineResult> {
     }
 
     @Override
-    public TableResult handleTable(List<CombineResult> results, StatsTimeUnit unit) {
+    public  List<TableResult> handleTable(List<CombineResult> results, StatsTimeUnit unit) {
         TableResult result = new TableResult();
         //根据时间对结果进行区分统计
         Map<String, List<CombineResult>> groupByTime = groupByTime(results, unit);
@@ -185,6 +164,6 @@ public class PcaStatsAnalyse implements CommonStats<CombineResult> {
             contents.add(contentValues);
         });
 
-        return result;
+        return Arrays.asList(result);
     }
 }

+ 272 - 0
coffee-system/src/main/java/com/coffee/bus/stats/entity/EvalTableResult.java

@@ -0,0 +1,272 @@
+package com.coffee.bus.stats.entity;
+
+import com.coffee.bus.service.dto.CombineEvalResult;
+
+import java.util.HashMap;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName EvalTableResult.java
+ * @Description TODO
+ * @createTime 2022年06月08日 22:40:00
+ */
+public class EvalTableResult extends HashMap<String,Long> {
+    public Long getStatics(){ return this.computeIfAbsent("静息疼痛",k->0L); }
+
+    public Long getActivity(){
+        return this.computeIfAbsent("活动疼痛",k->0L);
+    }
+
+    public Long getCalm(){
+        return this.computeIfAbsent("镇静评分",k->0L);
+    }
+
+    public Long getLeftArm(){
+        return this.computeIfAbsent("左上肢",k->0L);
+    }
+
+    public Long getLeftLeg(){
+        return this.computeIfAbsent("左下肢",k->0L);
+    }
+
+    public Long getRightArm(){
+        return this.computeIfAbsent("右上肢",k->0L);
+    }
+
+    public Long getRightLeg(){
+        return this.computeIfAbsent("右下肢",k->0L);
+    }
+
+    public Long getNauseaVomit(){
+        return this.computeIfAbsent("恶心呕吐",k->0L);
+    }
+
+    public Long getItch(){ return this.computeIfAbsent("瘙痒",k->0L); }
+
+    public Long getVertigo(){
+        return this.computeIfAbsent("眩晕",k->0L);
+    }
+
+    public Long getSoreThroat(){
+        return this.computeIfAbsent("咽喉疼痛",k->0L);
+    }
+
+    public Long getUroschesis(){
+        return this.computeIfAbsent("尿潴留",k->0L);
+    }
+
+    public Long getBreathDepression(){
+        return this.computeIfAbsent("呼吸抑制",k->0L);
+    }
+
+    public Long getHoarseness(){
+        return this.computeIfAbsent("声音嘶哑",k->0L);
+    }
+
+    public Long getCognitionObstacle(){
+        return this.computeIfAbsent("认知障碍",k->0L);
+    }
+
+    public Long getShrinkPressure(){
+        return this.computeIfAbsent("收缩压",k->0L);
+    }
+
+    public Long getDiastolicPressure(){
+        return this.computeIfAbsent("舒张压",k->0L);
+    }
+
+    public Long getHeartRate(){
+        return this.computeIfAbsent("心率",k->0L);
+    }
+
+    public Long getFetalHeartRate(){
+        return this.computeIfAbsent("胎心",k->0L);
+    }
+
+    public Long getBreathRate(){
+        return this.computeIfAbsent("呼吸频率",k->0L);
+    }
+
+    public Long getBloodOxygenSaturation(){
+        return this.computeIfAbsent("血氧饱和度",k->0L);
+    }
+
+    public void incrementStatics() {
+        incrementValue("静息疼痛");
+    }
+
+    public void incrementActivity() {
+        incrementValue("活动疼痛");
+    }
+
+    public void incrementCalm() {
+        incrementValue("镇静评分");
+    }
+
+    public void incrementLeftArm() {
+        incrementValue("左上肢");
+    }
+
+    public void incrementLeftLeg() {
+        incrementValue("左下肢");
+    }
+
+    public void incrementRightArm() {
+        incrementValue("右上肢");
+    }
+
+    public void incrementRightLeg() {
+        incrementValue("右下肢");
+    }
+
+    public void incrementNauseaVomit() {
+        incrementValue("恶心呕吐");
+    }
+
+    public void incrementItch() {
+        incrementValue("瘙痒");
+    }
+
+    public void incrementVertigo() {
+        incrementValue("眩晕");
+    }
+
+    public void incrementSoreThroat() {
+        incrementValue("咽喉疼痛");
+    }
+
+    public void incrementUroschesis() {
+        incrementValue("尿潴留");
+    }
+
+    public void incrementBreathDepression() {
+        incrementValue("呼吸抑制");
+    }
+
+    public void incrementHoarseness() {
+        incrementValue("声音嘶哑");
+    }
+
+    public void incrementCognitionObstacle() {
+        incrementValue("认知障碍");
+    }
+
+    public void incrementShrinkPressure() {
+        incrementValue("收缩压");
+    }
+
+    public void incrementDiastolicPressure() {
+        incrementValue("舒张压");
+    }
+
+    public void incrementHeartRate() {
+        incrementValue("心率");
+    }
+
+    public void incrementFetalHeartRate() {
+        incrementValue("胎心");
+    }
+
+    public void incrementBreathRate() {
+        incrementValue("呼吸频率");
+    }
+
+    public void incrementBloodOxygenSaturation() {
+        incrementValue("血氧饱和度");
+    }
+
+    public EvalTableResult() {
+        getStatics();
+        getActivity();
+        getCalm();
+        getLeftArm();
+        getLeftLeg();
+        getRightArm();
+        getRightLeg();
+        getNauseaVomit();
+        getItch();
+        getVertigo();
+        getSoreThroat();
+        getUroschesis();
+        getBreathDepression();
+        getHoarseness();
+        getCognitionObstacle();
+        getShrinkPressure();
+        getDiastolicPressure();
+        getHeartRate();
+        getFetalHeartRate();
+        getBreathRate();
+        getBloodOxygenSaturation();
+    }
+
+    private void incrementValue(String name){
+        this.merge(name,1L,(t1,t2)->t1+t2);
+    }
+
+    public void handle(CombineEvalResult evalResult) {
+        if(evalResult.getStatics()!=null){
+            incrementStatics();
+        }
+        if(evalResult.getActivity()!=null){
+            incrementActivity();
+        }
+        if(evalResult.getCalm()!=null){
+            incrementCalm();
+        }
+        if(evalResult.getLeftArm()!=null){
+            incrementLeftArm();
+        }
+        if(evalResult.getLeftLeg()!=null){
+            incrementLeftLeg();
+        }
+        if(evalResult.getRightArm()!=null){
+            incrementRightArm();
+        }
+        if(evalResult.getRightLeg()!=null){
+            incrementRightLeg();
+        }
+        if(evalResult.getNauseaVomit()!=null){
+            incrementNauseaVomit();
+        }
+        if(evalResult.getItch()!=null){
+            incrementItch();
+        }
+        if(evalResult.getVertigo()!=null){
+            incrementVertigo();
+        }
+        if(evalResult.getSoreThroat()!=null){
+            incrementSoreThroat();
+        }
+        if(evalResult.getUroschesis()!=null){
+            incrementUroschesis();
+        }
+        if(evalResult.getBreathDepression()!=null){
+            incrementBreathDepression();
+        }
+        if(evalResult.getHoarseness()!=null){
+            incrementHoarseness();
+        }
+        if(evalResult.getCognitionObstacle()!=null){
+            incrementCognitionObstacle();
+        }
+        if(evalResult.getShrinkPressure()!=null){
+            incrementShrinkPressure();
+        }
+        if(evalResult.getDiastolicPressure()!=null){
+            incrementDiastolicPressure();
+        }
+        if(evalResult.getHeartRate()!=null){
+            incrementHeartRate();
+        }
+        if(evalResult.getFetalHeartRate()!=null){
+            incrementFetalHeartRate();
+        }
+        if(evalResult.getBreathRate()!=null){
+            incrementBreathRate();
+        }
+        if(evalResult.getBloodOxygenSaturation()!=null){
+            incrementBloodOxygenSaturation();
+        }
+    }
+}

+ 27 - 4
coffee-system/src/main/java/com/coffee/bus/stats/entity/LineResult.java

@@ -5,6 +5,7 @@ import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 
+import java.math.BigDecimal;
 import java.util.*;
 /**
  * @author lifang
@@ -20,21 +21,43 @@ public class LineResult<V> {
     private String id;
     private List<LineContent<V>> content;
     private List<String> time;
+
+    @ApiModelProperty("是否为比率图")
+    private boolean ratio;
+    @ApiModelProperty("是否为柱状图")
+    private boolean bar;
+    @ApiModelProperty("样本总数")
+    private Integer sampleCount;
+    @ApiModelProperty("最大值")
+    private BigDecimal max;
+    @ApiModelProperty("最小值")
+    private BigDecimal min;
+    @ApiModelProperty("方差")
+    private BigDecimal variance;
+    @ApiModelProperty("平均值")
+    private BigDecimal average;
+    @ApiModelProperty("图表介绍")
     private String description;
 
+    public static <T> LineResult<T> of(String id, boolean ratio, boolean bar, String description) {
+        return new LineResult<T>(id,new ArrayList<>(),new ArrayList<>(),ratio,bar,null,null,null,null,null,description);
+    }
 
     @Data
     public static class  LineContent<V>{
         private String id;
+        @ApiModelProperty("仅在比率图和柱状图时存在")
         private List<V> value;
+        @ApiModelProperty("样本总数")
+        private List<Integer> sampleCount;
         @ApiModelProperty("最大值")
-        private boolean max;
+        private List<BigDecimal> max;
         @ApiModelProperty("最小值")
-        private boolean min;
+        private List<BigDecimal> min;
         @ApiModelProperty("方差")
-        private boolean variance;
+        private List<List<BigDecimal>> variance;
         @ApiModelProperty("平均值")
-        private boolean average;
+        private List<BigDecimal> average;
 
         public LineContent(String id) {
             this.id = id;

+ 5 - 1
coffee-system/src/main/java/com/coffee/bus/stats/entity/StatsAnalyseResult.java

@@ -16,5 +16,9 @@ import java.util.*;
 public class StatsAnalyseResult {
     private List<PieResult> pie;
     private List<LineResult> line;
-    private TableResult table;
+    private List<TableResult> table;
+
+    public static StatsAnalyseResult empty(){
+        return of(new ArrayList<>(),new ArrayList<>(),new ArrayList<>());
+    }
 }

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

@@ -19,7 +19,7 @@ public class StatsColumn {
     private String title;
     @ApiModelProperty(value = "列名属性名称",hidden = true)
     private String dataIndex;
-    @JsonIgnore
+//    @JsonIgnore
     @ApiModelProperty(value = "是否为比率",hidden = true)
     private boolean isRatio;
 

+ 7 - 2
coffee-system/src/main/java/com/coffee/bus/stats/entity/TableResult.java

@@ -3,7 +3,6 @@ package com.coffee.bus.stats.entity;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.AllArgsConstructor;
 import lombok.Data;
-import lombok.NoArgsConstructor;
 
 import java.util.*;
 /**
@@ -15,13 +14,19 @@ import java.util.*;
  */
 @Data
 @AllArgsConstructor
-@NoArgsConstructor
 public class TableResult {
 
+    private String id;
+
     @ApiModelProperty("图表列名")
     private List<StatsColumn> column;
 
     @ApiModelProperty("图表内容 时间从大到小 一个map是一行数据")
     private List<Map<String,Object>> content;
 
+    private String description;
+
+    public TableResult() {
+        this.id="default";
+    }
 }

+ 43 - 0
coffee-system/src/main/java/com/coffee/bus/stats/enums/AgeStatsEnum.java

@@ -0,0 +1,43 @@
+package com.coffee.bus.stats.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AgeStatsEnum.java
+ * @Description 年龄分布枚举
+ * @createTime 2022年06月08日 10:11:00
+ */
+@AllArgsConstructor
+@Getter
+public enum  AgeStatsEnum {
+    infant("婴幼儿0-3岁",0,3),
+    child("儿童4-12岁",4,12),
+    juvenile("少年13-18岁",13,18),
+    youth("青年19-44岁",19,44),
+    midlife("中年45-59岁",45,59),
+    fewOld("年轻的老人60-74岁",60,74),
+    old("老年人75-89岁",75,89),
+    longevity("长寿的老人90岁以上",90,500),
+    unkown("未填写",-1,-1)
+    ;
+    private String name;
+    private Integer downLimit;
+    private Integer upLimit;
+
+    public static AgeStatsEnum judgeAge(Integer age){
+        if(age==null){
+            return unkown;
+        }
+        AgeStatsEnum[] values = AgeStatsEnum.values();
+        for (AgeStatsEnum value : values) {
+            if((value.getUpLimit().compareTo(age)>=0)&&(value.getDownLimit().compareTo(age)<0)){
+                return value;
+            }
+        }
+        return unkown;
+    }
+
+}

+ 37 - 0
coffee-system/src/main/java/com/coffee/bus/stats/enums/WeightRangeEnum.java

@@ -0,0 +1,37 @@
+package com.coffee.bus.stats.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.math.BigDecimal;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName WeightRangeEnum.java
+ * @Description TODO
+ * @createTime 2022年06月08日 11:24:00
+ */
+@AllArgsConstructor
+@Getter
+public enum  WeightRangeEnum {
+    light("小于50kg",BigDecimal.ZERO,BigDecimal.valueOf(50)),
+    normal("50-70kg",BigDecimal.valueOf(50),BigDecimal.valueOf(70)),
+    heavy("大于70kg",BigDecimal.valueOf(70),BigDecimal.valueOf(1000)),
+    unkown("未知",BigDecimal.valueOf(-1),BigDecimal.valueOf(-1));
+    private String name;
+    private BigDecimal downLimit;
+    private BigDecimal upLimit;
+    public static WeightRangeEnum judgeWeight(BigDecimal weight){
+        if(weight==null){
+            return unkown;
+        }
+        WeightRangeEnum[] values = WeightRangeEnum.values();
+        for (WeightRangeEnum value : values) {
+            if((value.getUpLimit().compareTo(weight)>=0)&&(value.getDownLimit().compareTo(weight)<0)){
+                return value;
+            }
+        }
+        return unkown;
+    }
+}

+ 181 - 0
coffee-system/src/main/java/com/coffee/bus/stats/report/AgeAndGenderStatsReport.java

@@ -0,0 +1,181 @@
+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.StatsColumn;
+import com.coffee.bus.stats.entity.TableResult;
+import com.coffee.bus.stats.enums.AgeStatsEnum;
+import com.coffee.common.enums.SexEnum;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.*;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AgeAndGenderReport.java
+ * @Description TODO
+ * @createTime 2022年06月08日 13:41:00
+ */
+@Slf4j
+@AllArgsConstructor
+@Service
+public class AgeAndGenderStatsReport 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.genderAndAge;
+    }
+
+    @Override
+    public List<PieResult> handlePie(List<CombineResult> results) {
+        PieResult manResult = PieResult.of("man","男性统计饼图");
+        PieResult womanResult = PieResult.of("woman","女性统计饼图");
+        Map<AgeStatsEnum, Integer> manAgeMap = new HashMap<>();
+        Map<AgeStatsEnum, Integer> womanAgeMap = new HashMap<>();
+        for (AgeStatsEnum value : AgeStatsEnum.values()) {
+            manAgeMap.put(value,0);
+            womanAgeMap.put(value,0);
+        }
+        for (CombineResult combineResult : results) {
+            SexEnum gender = combineResult.getPatientGender();
+            if(gender!=null){
+                if(SexEnum.WOMAN.equals(gender)){
+                    womanAgeMap.merge(AgeStatsEnum.judgeAge(combineResult.getPatientAge()),1,(t1,t2)->t1+t2);
+                } else if (SexEnum.MAN.equals(gender)) {
+                    manAgeMap.merge(AgeStatsEnum.judgeAge(combineResult.getPatientAge()),1,(t1,t2)->t1+t2);
+                }
+            }
+
+        }
+        womanAgeMap.forEach((ageEnum,count)->{
+            womanResult.addContent(ageEnum.getName(),count);
+        });
+        manAgeMap.forEach((ageEnum,count)->{
+            manResult.addContent(ageEnum.getName(),count);
+        });
+        return Arrays.asList(manResult,womanResult);
+    }
+
+    @Override
+    public List<LineResult> handleLine(List<CombineResult> results, StatsTimeUnit unit) {
+        //根据时间对结果进行区分统计
+        Map<String, List<CombineResult>> groupByTime = groupByTime(results, unit);
+        LineResult<BigDecimal> manResult =LineResult.of("manAverageLine",false,false,"男性年龄走势图");
+        handleMixPolyLines(groupByTime,manResult,
+                peek->{},
+                filter->filter.getPatientAge()!=null&&SexEnum.MAN.equals(filter.getPatientGender()),
+                map->BigDecimal.valueOf(map.getPatientAge())
+        );
+        LineResult<BigDecimal> womanResult =LineResult.of("womanAverageLine",false,false,"女性年龄走势图");
+        handleMixPolyLines(groupByTime,womanResult,
+                peek->{},
+                filter->filter.getPatientAge()!=null&&SexEnum.WOMAN.equals(filter.getPatientGender()),
+                map->BigDecimal.valueOf(map.getPatientAge())
+        );
+        //比率
+        return Arrays.asList(manResult,womanResult);
+    }
+
+    @Override
+    public  List<TableResult> handleTable(List<CombineResult> results, StatsTimeUnit unit) {
+        Map<String, List<CombineResult>> groupByTime = groupByTime(results, unit);
+        TableResult manTableResult = new TableResult();
+        manTableResult.setId("manTable");
+        manTableResult.setDescription("男性年龄分布表格");
+        handleTable(groupByTime,SexEnum.MAN,manTableResult);
+        TableResult womanTableResult = new TableResult();
+        womanTableResult.setId("womanTable");
+        womanTableResult.setDescription("女性年龄分布表格");
+        handleTable(groupByTime,SexEnum.WOMAN,womanTableResult);
+        return Arrays.asList(manTableResult,womanTableResult);
+    }
+
+
+    private   TableResult handleTable( Map<String, List<CombineResult>> groupByTime , SexEnum gender,TableResult result) {
+        //根据时间对结果进行区分统计
+        List<StatsColumn> allColumn = Arrays.asList(
+                StatsColumn.of("时间","time"),
+                StatsColumn.of("总人数","total"),
+                StatsColumn.of(AgeStatsEnum.infant.getName(),AgeStatsEnum.infant.getName()),
+                StatsColumn.of("婴幼儿","婴幼儿比率",true),
+                StatsColumn.of(AgeStatsEnum.child.getName(),AgeStatsEnum.child.getName()),
+                StatsColumn.of("儿童","儿童比率",true),
+                StatsColumn.of(AgeStatsEnum.juvenile.getName(),AgeStatsEnum.juvenile.getName()),
+                StatsColumn.of("少年","少年比率",true),
+                StatsColumn.of(AgeStatsEnum.youth.getName(),AgeStatsEnum.youth.getName()),
+                StatsColumn.of("青年","青年比率",true),
+                StatsColumn.of(AgeStatsEnum.midlife.getName(),AgeStatsEnum.midlife.getName()),
+                StatsColumn.of("中年","中年比率",true),
+                StatsColumn.of(AgeStatsEnum.fewOld.getName(),AgeStatsEnum.fewOld.getName()),
+                StatsColumn.of("年轻的老人","年轻的老人比率",true),
+                StatsColumn.of(AgeStatsEnum.old.getName(),AgeStatsEnum.old.getName()),
+                StatsColumn.of("老年人","老年人比率",true),
+                StatsColumn.of(AgeStatsEnum.longevity.getName(),AgeStatsEnum.longevity.getName()),
+                StatsColumn.of("长寿的老年人","长寿的老年人比率",true),
+                StatsColumn.of(AgeStatsEnum.unkown.getName(),AgeStatsEnum.unkown.getName()),
+                StatsColumn.of("未填写","未填写比率",true)
+
+        );
+        List<Map<String, Object>> contents = new ArrayList<>();
+        result.setContent(contents);
+        result.setColumn(allColumn);
+        groupByTime.forEach((timeRange,combineResults)->{
+            LinkedHashMap<String, Object> contentValues = new LinkedHashMap<>();
+            Map<AgeStatsEnum, Integer> ageMap = new HashMap<>();
+            long total=0;
+            for (AgeStatsEnum value : AgeStatsEnum.values()) {
+                ageMap.put(value,0);
+            }
+            for (CombineResult combineResult : combineResults) {
+                if (gender.equals(combineResult.getPatientGender())) {
+                    ageMap.merge(AgeStatsEnum.judgeAge(combineResult.getPatientAge()),1,(t1,t2)->t1+t2);
+                    ++total;
+                }
+            }
+            contentValues.put("time",timeRange);
+            contentValues.put("total",total);
+            contentValues.put(AgeStatsEnum.infant.getName(),ageMap.get(AgeStatsEnum.infant));
+            contentValues.put(AgeStatsEnum.child.getName(),ageMap.get(AgeStatsEnum.child));
+            contentValues.put(AgeStatsEnum.juvenile.getName(),ageMap.get(AgeStatsEnum.juvenile));
+            contentValues.put(AgeStatsEnum.youth.getName(),ageMap.get(AgeStatsEnum.youth));
+            contentValues.put(AgeStatsEnum.midlife.getName(),ageMap.get(AgeStatsEnum.midlife));
+            contentValues.put(AgeStatsEnum.fewOld.getName(),ageMap.get(AgeStatsEnum.fewOld));
+            contentValues.put(AgeStatsEnum.old.getName(),ageMap.get(AgeStatsEnum.old));
+            contentValues.put(AgeStatsEnum.longevity.getName(),ageMap.get(AgeStatsEnum.longevity));
+            contentValues.put(AgeStatsEnum.unkown.getName(),ageMap.get(AgeStatsEnum.unkown));
+            //%比字段一定要以  比率 结尾,以便前端统一识别!
+            contentValues.put("婴幼儿比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.infant)),BigDecimal.valueOf(total)));
+            contentValues.put("儿童比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.child)),BigDecimal.valueOf(total)));
+            contentValues.put("少年比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.juvenile)),BigDecimal.valueOf(total)));
+            contentValues.put("青年比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.youth)),BigDecimal.valueOf(total)));
+            contentValues.put("中年比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.midlife)),BigDecimal.valueOf(total)));
+            contentValues.put("年轻的老人比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.fewOld)),BigDecimal.valueOf(total)));
+            contentValues.put("老年人比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.old)),BigDecimal.valueOf(total)));
+            contentValues.put("长寿的老年人比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.longevity)),BigDecimal.valueOf(total)));
+            contentValues.put("未填写比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.unkown)),BigDecimal.valueOf(total)));
+
+            contents.add(contentValues);
+        });
+
+        return result;
+    }
+}

+ 170 - 0
coffee-system/src/main/java/com/coffee/bus/stats/report/AgeStatsReport.java

@@ -0,0 +1,170 @@
+package com.coffee.bus.stats.report;
+
+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.StatsColumn;
+import com.coffee.bus.stats.entity.TableResult;
+import com.coffee.bus.stats.enums.AgeStatsEnum;
+import com.coffee.bus.stats.enums.PieEnum;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AgeStatsReport.java
+ * @Description TODO
+ * @createTime 2022年06月08日 10:07:00
+ */
+@AllArgsConstructor
+@Service
+public class AgeStatsReport 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.age;
+    }
+
+    /**
+     * 描述: 饼图分析
+     * 0-3岁  婴儿
+     * 4-12岁 儿童
+     * 13-18岁 少年
+     * 19-44岁 青年
+     * 45-59岁 中年
+     * 60-74岁 年轻的老人
+     * 75-89岁 老年人
+     * 90岁以上 长寿的老人
+     * 未填写
+     * @author lifang
+     * @date 2022/6/8 10:09
+     * @param results
+     * @return List<PieResult>
+     */
+    @Override
+    public List<PieResult> handlePie(List<CombineResult> results) {
+        PieResult result = PieResult.of(PieEnum.total.name(),PieEnum.total.getText());
+        Map<AgeStatsEnum, Integer> ageMap = new HashMap<>();
+        for (AgeStatsEnum value : AgeStatsEnum.values()) {
+            ageMap.put(value,0);
+        }
+        for (CombineResult combineResult : results) {
+            ageMap.merge(AgeStatsEnum.judgeAge(combineResult.getPatientAge()),1,(t1,t2)->t1+t2);
+        }
+        ageMap.forEach((ageEnum,count)->{
+            result.addContent(ageEnum.getName(),count);
+        });
+        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(averageLine(groupByTime));
+        //比率
+        return result;
+    }
+
+    private LineResult averageLine(Map<String, List<CombineResult>> groupByTime) {
+        LineResult<BigDecimal> result = LineResult.of("averageLine",false,false,"平均年龄走势图");
+        handleMixPolyLines(groupByTime,
+                result,
+                combineResult -> {},
+                combineResult -> combineResult.getPatientAge()!=null,
+                combineResult -> BigDecimal.valueOf(combineResult.getPatientAge()));
+        return result;
+    }
+
+    @Override
+    public  List<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("总人数","total"),
+
+                StatsColumn.of(AgeStatsEnum.infant.getName(),AgeStatsEnum.infant.getName()),
+                StatsColumn.of("婴幼儿比率","婴幼儿比率"),
+                StatsColumn.of(AgeStatsEnum.child.getName(),AgeStatsEnum.child.getName()),
+                StatsColumn.of("儿童比率","儿童比率"),
+                StatsColumn.of(AgeStatsEnum.juvenile.getName(),AgeStatsEnum.juvenile.getName()),
+                StatsColumn.of("少年比率","少年比率"),
+                StatsColumn.of(AgeStatsEnum.youth.getName(),AgeStatsEnum.youth.getName()),
+                StatsColumn.of("青年比率","青年比率"),
+                StatsColumn.of(AgeStatsEnum.midlife.getName(),AgeStatsEnum.midlife.getName()),
+                StatsColumn.of("中年比率","中年比率"),
+                StatsColumn.of(AgeStatsEnum.fewOld.getName(),AgeStatsEnum.fewOld.getName()),
+                StatsColumn.of("年轻的老人比率","年轻的老人比率"),
+                StatsColumn.of(AgeStatsEnum.old.getName(),AgeStatsEnum.old.getName()),
+                StatsColumn.of("老年人比率","老年人比率"),
+                StatsColumn.of(AgeStatsEnum.longevity.getName(),AgeStatsEnum.longevity.getName()),
+                StatsColumn.of("长寿的老年人比率","长寿的老年人比率"),
+                StatsColumn.of(AgeStatsEnum.unkown.getName(),AgeStatsEnum.unkown.getName()),
+                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<>();
+            Map<AgeStatsEnum, Integer> ageMap = new HashMap<>();
+            BigDecimal total=BigDecimal.valueOf(CollectionUtil.size(combineResults));
+            for (AgeStatsEnum value : AgeStatsEnum.values()) {
+                ageMap.put(value,0);
+            }
+            for (CombineResult combineResult : combineResults) {
+                ageMap.merge(AgeStatsEnum.judgeAge(combineResult.getPatientAge()),1,(t1,t2)->t1+t2);
+            }
+            contentValues.put("time",timeRange);
+            contentValues.put("total",total);
+            contentValues.put(AgeStatsEnum.infant.getName(),ageMap.get(AgeStatsEnum.infant));
+            contentValues.put(AgeStatsEnum.child.getName(),ageMap.get(AgeStatsEnum.child));
+            contentValues.put(AgeStatsEnum.juvenile.getName(),ageMap.get(AgeStatsEnum.juvenile));
+            contentValues.put(AgeStatsEnum.youth.getName(),ageMap.get(AgeStatsEnum.youth));
+            contentValues.put(AgeStatsEnum.midlife.getName(),ageMap.get(AgeStatsEnum.midlife));
+            contentValues.put(AgeStatsEnum.fewOld.getName(),ageMap.get(AgeStatsEnum.fewOld));
+            contentValues.put(AgeStatsEnum.old.getName(),ageMap.get(AgeStatsEnum.old));
+            contentValues.put(AgeStatsEnum.longevity.getName(),ageMap.get(AgeStatsEnum.longevity));
+            contentValues.put(AgeStatsEnum.unkown.getName(),ageMap.get(AgeStatsEnum.unkown));
+            //%比字段一定要以  比率 结尾,以便前端统一识别!
+            contentValues.put("婴幼儿比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.infant)),total));
+            contentValues.put("儿童比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.child)),total));
+            contentValues.put("少年比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.juvenile)),total));
+            contentValues.put("青年比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.youth)),total));
+            contentValues.put("中年比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.midlife)),total));
+            contentValues.put("年轻的老人比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.fewOld)),total));
+            contentValues.put("老年人比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.old)),total));
+            contentValues.put("长寿的老年人比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.longevity)),total));
+            contentValues.put("未填写比率",computeRatio(BigDecimal.valueOf(ageMap.get(AgeStatsEnum.unkown)),total));
+
+            contents.add(contentValues);
+        });
+
+        return Arrays.asList(result);
+    }
+}

+ 183 - 0
coffee-system/src/main/java/com/coffee/bus/stats/report/AsaStatsReport.java

@@ -0,0 +1,183 @@
+package com.coffee.bus.stats.report;
+
+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.TableResult;
+import com.coffee.bus.stats.enums.PieEnum;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName AsaReport.java
+ * @Description TODO
+ * @createTime 2022年06月08日 14:34:00
+ */
+@Slf4j
+@AllArgsConstructor
+@Service
+public class AsaStatsReport implements CommonStats<CombineResult> {
+    private final LocalBusInfusionHistoryService infusionHistoryService;
+    private final LocalBusConMixService conMixService;
+    @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.asa;
+    }
+
+    @Override
+    public List<PieResult> handlePie(List<CombineResult> results) {
+        PieResult result = PieResult.of(PieEnum.total.name(),PieEnum.total.getText());
+        List<BusConMixEntity> conAsa = getConAsa();
+        Map<String, Long> asaMap =groupByAsa(results);
+        for (BusConMixEntity busConMixEntity : conAsa) {
+            asaMap.putIfAbsent(busConMixEntity.getName(),0L);
+        }
+        asaMap.forEach(result::addContent);
+        return Arrays.asList(result);
+    }
+
+    @Override
+    public List<LineResult> handleLine(List<CombineResult> results, StatsTimeUnit unit) {
+        //根据时间对结果进行区分统计
+        Map<String, List<CombineResult>> groupByTime = groupByTime(results, unit);
+        //获取所有镇痛信息
+        List<String> allAsa = getAllAsaName( groupByAsa(results), getConAsa());
+        List<LineResult> result = new ArrayList<>();
+        result.add(countLine(groupByTime,allAsa));
+        result.add(ratioLine(groupByTime,allAsa));
+        return result;
+    }
+
+    private Map<String,Long> groupByAsa(List<CombineResult> results){
+        return  results.stream()
+                .peek(result -> {
+                    //将空的asa置为未填写
+                    if (StrUtil.isNullOrUndefined(result.getAsa())) {
+                        result.setAsa("未填写");
+                    }
+                })
+                .collect(Collectors.groupingBy(CombineResult::getAsa,Collectors.counting()));
+    }
+
+
+    @Override
+    public List<TableResult> handleTable(List<CombineResult> results, StatsTimeUnit unit) {
+        return null;
+    }
+
+    /**
+     * 描述: 获取当前医院下的所有ASA
+     * @author lifang
+     * @date 2022/6/6 11:23
+     * @param
+     * @return List<BusConMixEntity>
+     */
+    private  List<BusConMixEntity> getConAsa(){
+        return  conMixService. list(new QueryWrapper<BusConMixEntity>().lambda().eq(BusConMixEntity::getType, ConstantMixEnum.asa));
+    }
+
+
+    private List<String> getAllAsaName(Map<String,?> asaMaps,List<BusConMixEntity> conAsa){
+        List<String> keys = new LinkedList<>();
+        if (CollectionUtil.isNotEmpty(conAsa)&&CollectionUtil.isNotEmpty(asaMaps)) {
+            //取得并集
+            keys.addAll(CollectionUtil.union(asaMaps.keySet(),
+                    conAsa
+                            .stream()
+                            .map(BusConMixEntity::getName).collect(Collectors.toList())));
+        } else if(CollectionUtil.isNotEmpty(conAsa)){
+            keys=conAsa.stream().map(BusConMixEntity::getName).collect(Collectors.toList());
+        }else if(CollectionUtil.isNotEmpty(asaMaps)){
+            keys.addAll(asaMaps.keySet());
+        }
+        return keys;
+    }
+
+    private LineResult<Long> countLine( Map<String, List<CombineResult>> groupByTime, List<String> allAsa){
+        LineResult<Long> lineResult = LineResult.of("count",false,true,"ASA数量分布图");
+        //keys由历史数据和常量维护信息表并集构成
+        Map<String, LineResult.LineContent<Long>> content = new HashMap<>();
+        List<String> time = lineResult.getTime();
+        //根据时间区间对镇痛方式进行统计
+        groupByTime.forEach((timeRange,combineResult)->{
+            //已有的镇痛方式数量
+            Map<String, Long> groupByAsa = groupByAsa(combineResult);
+            //获取特定时间区间内 各镇痛方式的 数量
+            allAsa.forEach(asaName->{
+                //折线图内容
+                LineResult.LineContent<Long> lineContent = content.computeIfAbsent(asaName, k -> new LineResult.LineContent<>(k));
+                List<Long> values = Optional.ofNullable(lineContent.getValue()).orElse(new ArrayList<>());
+                Long analCount = groupByAsa.get(asaName);
+                if(analCount==null||analCount==0){
+                    values.add(0L);
+                }else {
+                    values.add(analCount);
+                }
+                lineContent.setValue(values);
+            });
+            time.add(timeRange);
+        });
+        lineResult.setContent(new ArrayList<>(content.values()));
+        return lineResult;
+    }
+
+
+    private LineResult ratioLine( Map<String, List<CombineResult>> groupByTime, List<String> allAsa) {
+        LineResult<BigDecimal> result =LineResult.of("ratio",true,false,"ASA占比分布图");
+        Map<String, LineResult.LineContent<BigDecimal>> content = new HashMap<>();
+        result.setContent(new ArrayList<>(content.values()));
+        List<String> time = result.getTime();
+
+        //根据时间区间对镇痛方式进行统计
+        groupByTime.forEach((timeRange,combineResult)->{
+            //已有的镇痛方式数量
+            BigDecimal totalSize = BigDecimal.valueOf(CollectionUtil.size(combineResult));
+            Map<String, Long> groupByAnal = groupByAsa(combineResult);
+            //获取特定时间区间内 各镇痛方式的 数量
+            allAsa.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;
+    }
+}

+ 150 - 8
coffee-system/src/main/java/com/coffee/bus/stats/report/GenderStatsReport.java

@@ -1,5 +1,6 @@
 package com.coffee.bus.stats.report;
 
+import cn.hutool.core.collection.CollectionUtil;
 import com.coffee.bus.enums.StatsAnalyseEnum;
 import com.coffee.bus.enums.StatsTimeUnit;
 import com.coffee.bus.service.LocalBusInfusionHistoryService;
@@ -8,14 +9,15 @@ 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 com.coffee.common.enums.SexEnum;
 import lombok.AllArgsConstructor;
 import org.springframework.stereotype.Service;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.math.BigDecimal;
+import java.util.*;
 
 /**
  * @author lifang
@@ -44,16 +46,156 @@ public class GenderStatsReport implements CommonStats<CombineResult> {
 
     @Override
     public List<PieResult> handlePie(List<CombineResult> results) {
-        return null;
+        PieResult result = PieResult.of(PieEnum.total.name(),PieEnum.total.getText());
+        long man=0;
+        long woman=0;
+        long unkown=0;
+        for (CombineResult combineResult : results) {
+            SexEnum gender = combineResult.getPatientGender();
+            if(gender==null||SexEnum.UNKNOW.equals(gender)){
+                unkown++;
+                continue;
+            }
+            if (SexEnum.MAN.equals(gender)) {
+                man++;
+                continue;
+            }
+            if (SexEnum.WOMAN.equals(gender)) {
+                woman++;
+                continue;
+            }
+        }
+        result.addContent("男性",man);
+        result.addContent("女性",woman);
+        result.addContent("未知",unkown);
+        return Arrays.asList(result);
     }
 
     @Override
     public List<LineResult> handleLine(List<CombineResult> results, StatsTimeUnit unit) {
-        return null;
+        //根据时间对结果进行区分统计
+        Map<String, List<CombineResult>> groupByTime = groupByTime(results, unit);
+        List<LineResult> result = new ArrayList<>();
+        result.add(countLine(groupByTime));
+        result.add(ratioLine(groupByTime));
+        //比率
+        return result;
+    }
+
+    private LineResult ratioLine(Map<String, List<CombineResult>> groupByTime) {
+        LineResult<BigDecimal> result = LineResult.of("ratio",true,false,"性别占比分布图");
+        List<String> time = result.getTime();
+        LineResult.LineContent<BigDecimal> manLine = new LineResult.LineContent<>("男性比例");
+        LineResult.LineContent<BigDecimal> womanLine = new LineResult.LineContent<>("女性比例");
+        LineResult.LineContent<BigDecimal> unkownLine = new LineResult.LineContent<>("未知比例");
+        result.setContent(Arrays.asList(manLine,womanLine,unkownLine));
+        groupByTime.forEach((timeRange,combineResults)->{
+            long man=0;
+            long woman=0;
+            long unkown=0;
+            BigDecimal total=BigDecimal.valueOf(CollectionUtil.size(combineResults));
+            for (CombineResult combineResult : combineResults) {
+                SexEnum gender = combineResult.getPatientGender();
+                if(gender==null||SexEnum.UNKNOW.equals(gender)){
+                    ++unkown;
+                    continue;
+                }
+                if (SexEnum.MAN.equals(gender)) {
+                    ++man;
+                    continue;
+                }
+                if (SexEnum.WOMAN.equals(gender)) {
+                    ++woman;
+                    continue;
+                }
+            }
+            manLine.addValue(computeRatio(BigDecimal.valueOf(man),total));
+            womanLine.addValue(computeRatio(BigDecimal.valueOf(woman),total));
+            unkownLine.addValue(computeRatio(BigDecimal.valueOf(unkown),total));
+            time.add(timeRange);
+        });
+        return result;
+    }
+
+    private LineResult countLine(Map<String, List<CombineResult>> groupByTime) {
+        LineResult<Long> result = LineResult.of("count",false,true,"性别数量分布图");
+        List<String> time = result.getTime();
+        LineResult.LineContent<Long> manLine = new LineResult.LineContent<>("男性");
+        LineResult.LineContent<Long> womanLine = new LineResult.LineContent<>("女性");
+        LineResult.LineContent<Long> unkownLine = new LineResult.LineContent<>("未知");
+        result.setContent(Arrays.asList(manLine,womanLine,unkownLine));
+        groupByTime.forEach((timeRange,combineResults)->{
+            long man=0;
+            long woman=0;
+            long unkown=0;
+            for (CombineResult combineResult : combineResults) {
+                SexEnum gender = combineResult.getPatientGender();
+                if(gender==null||SexEnum.UNKNOW.equals(gender)){
+                    unkown++;
+                    continue;
+                }
+                if (SexEnum.MAN.equals(gender)) {
+                    man++;
+                    continue;
+                }
+                if (SexEnum.WOMAN.equals(gender)) {
+                    woman++;
+                    continue;
+                }
+            }
+            manLine.addValue(man);
+            womanLine.addValue(woman);
+            unkownLine.addValue(unkown);
+            time.add(timeRange);
+        });
+        return result;
     }
 
     @Override
-    public TableResult handleTable(List<CombineResult> results, StatsTimeUnit unit) {
-        return null;
+    public  List<TableResult> handleTable(List<CombineResult> results, StatsTimeUnit unit) {
+        TableResult result = new TableResult();
+        //根据时间对结果进行区分统计
+        Map<String, List<CombineResult>> groupByTime = groupByTime(results, unit);
+        List<StatsColumn> allColumn = getAllColumn(Arrays.asList("男性", "女性", "未知"));
+        allColumn.add(1,StatsColumn.of("总人数","total"));
+
+        List<Map<String, Object>> contents = new ArrayList<>();
+        result.setContent(contents);
+        result.setColumn(allColumn);
+
+        groupByTime.forEach((timeRange,combineResults)->{
+            LinkedHashMap<String, Object> contentValues = new LinkedHashMap<>();
+            long man=0;
+            long woman=0;
+            long unkown=0;
+            BigDecimal total=BigDecimal.valueOf(CollectionUtil.size(combineResults));
+            for (CombineResult combineResult : combineResults) {
+                SexEnum gender = combineResult.getPatientGender();
+                if(gender==null||SexEnum.UNKNOW.equals(gender)){
+                    ++unkown;
+                    continue;
+                }
+                if (SexEnum.MAN.equals(gender)) {
+                    ++man;
+                    continue;
+                }
+                if (SexEnum.WOMAN.equals(gender)) {
+                    ++woman;
+                    continue;
+                }
+            }
+
+            contentValues.put("time",timeRange);
+            contentValues.put("total",total);
+            contentValues.put("男性",man);
+            contentValues.put("男性比率",computeRatio(BigDecimal.valueOf(man),total));
+            contentValues.put("女性",woman);
+            contentValues.put("女性比率",computeRatio(BigDecimal.valueOf(woman),total));
+            contentValues.put("未知",unkown);
+            contentValues.put("未知比率",computeRatio(BigDecimal.valueOf(unkown),total));
+
+            contents.add(contentValues);
+        });
+        return Arrays.asList(result);
     }
 }

+ 176 - 0
coffee-system/src/main/java/com/coffee/bus/stats/report/WeightStatsReport.java

@@ -0,0 +1,176 @@
+package com.coffee.bus.stats.report;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
+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 com.coffee.bus.stats.enums.PieEnum;
+import com.coffee.bus.stats.enums.WeightRangeEnum;
+import com.coffee.common.enums.SexEnum;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName WeightReport.java
+ * @Description TODO
+ * @createTime 2022年06月08日 11:19:00
+ */
+@Slf4j
+@AllArgsConstructor
+@Service
+public class WeightStatsReport 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.weight;
+    }
+
+    @Override
+    public List<PieResult> handlePie(List<CombineResult> results) {
+        ArrayList<PieResult> result = new ArrayList<>();
+        result.add(totalPie(results));
+        PieResult manResult = PieResult.of("man","男性体重比例图");
+        result.add(genderPie(results,SexEnum.MAN,manResult));
+        PieResult womenResult = PieResult.of("women","女性体重比例图");
+        result.add(genderPie(results,SexEnum.WOMAN,womenResult));
+        return result;
+    }
+
+    private PieResult genderPie(List<CombineResult> results,SexEnum sex,PieResult result) {
+        List<CombineResult> genderResults = results.stream().filter(combineResult -> sex.equals(combineResult.getPatientGender()))
+                .collect(Collectors.toList());
+        Map<WeightRangeEnum, Integer> ageMap = new HashMap<>();
+        for (WeightRangeEnum value : WeightRangeEnum.values()) {
+            ageMap.put(value,0);
+        }
+        for (CombineResult combineResult : genderResults) {
+            String weight = combineResult.getWeight();
+            if(StrUtil.isBlank(weight)){
+                ageMap.merge(WeightRangeEnum.unkown,1,(t1, t2)->t1+t2);
+            }
+            try {
+                BigDecimal count = BigDecimal.valueOf(Double.valueOf(weight));
+                ageMap.merge(WeightRangeEnum.judgeWeight(count),1,(t1, t2)->t1+t2);
+            }catch (Exception e){
+                log.error("手术【{}】,体重格式错误【{}】",combineResult.getClinicId(),combineResult.getWeight());
+            }
+        }
+        ageMap.forEach((weightRangeEnum,count)->{
+            result.addContent(weightRangeEnum.getName(),count);
+        });
+        return result;
+    }
+
+    /**
+     * 描述: 体重比例总图
+     * @author lifang
+     * @date 2022/6/8 11:23
+     * @param results
+     * @return PieResult
+     */
+    private PieResult totalPie(List<CombineResult> results) {
+        PieResult result = PieResult.of(PieEnum.total.name(),PieEnum.total.getText());
+        Map<WeightRangeEnum, Integer> ageMap = new HashMap<>();
+        for (WeightRangeEnum value : WeightRangeEnum.values()) {
+            ageMap.put(value,0);
+        }
+        for (CombineResult combineResult : results) {
+            String weight = combineResult.getWeight();
+            if(StrUtil.isBlank(weight)){
+                ageMap.merge(WeightRangeEnum.unkown,1,(t1, t2)->t1+t2);
+            }
+            try {
+                BigDecimal count = BigDecimal.valueOf(Double.valueOf(weight));
+                ageMap.merge(WeightRangeEnum.judgeWeight(count),1,(t1, t2)->t1+t2);
+            }catch (Exception e){
+                log.error("手术【{}】,体重格式错误【{}】",combineResult.getClinicId(),combineResult.getWeight());
+            }
+        }
+        ageMap.forEach((weightRangeEnum,count)->{
+            result.addContent(weightRangeEnum.getName(),count);
+        });
+        return result;
+    }
+
+    @Override
+    public List<LineResult> handleLine(List<CombineResult> results, StatsTimeUnit unit) {
+        //根据时间对结果进行区分统计
+        Map<String, List<CombineResult>> groupByTime = groupByTime(results, unit);
+        List<LineResult> result = new ArrayList<>();
+        LineResult<BigDecimal> manLine = LineResult.of("manAverageLine",false,false,"男性平均体重走势图");
+        handleMixPolyLines(groupByTime,manLine,
+                peek->{},
+                filter->{
+                    if(filter.getWeight()==null){
+                        return false;
+                    }
+                    try {
+                        Double test = Double.valueOf(filter.getWeight());
+                    }catch (Exception e){
+                        log.error("临床【{}】体重【{}】格式错误",filter.getClinicId(),filter.getWeight());
+                        return false;
+                    }
+                    if (SexEnum.MAN.equals(filter.getPatientGender())) {
+                        return true;
+                    }
+                    return false;
+
+                },
+                map->BigDecimal.valueOf(Long.valueOf(map.getWeight()))
+        );
+        LineResult<BigDecimal> womenLine =  LineResult.of("womenAverageLine",false,false,"女性平均体重走势图");
+        handleMixPolyLines(groupByTime,womenLine,
+                peek->{},
+                filter->{
+                    if(filter.getWeight()==null){
+                        return false;
+                    }
+                    try {
+                        Double test = Double.valueOf(filter.getWeight());
+                    }catch (Exception e){
+                        log.error("临床【{}】体重【{}】格式错误",filter.getClinicId(),filter.getWeight());
+                        return false;
+                    }
+                    if (SexEnum.WOMAN.equals(filter.getPatientGender())) {
+                        return true;
+                    }
+                    return false;
+
+                },
+                map->BigDecimal.valueOf(Long.valueOf(map.getWeight()))
+        );
+        result.add(womenLine);
+        result.add(manLine);
+        //比率
+        return result;
+    }
+
+    @Override
+    public  List<TableResult> handleTable(List<CombineResult> results, StatsTimeUnit unit) {
+        return null;
+    }
+}

+ 13 - 3
coffee-system/src/main/java/com/coffee/bus/web/handler/CheckTimeHandler.java

@@ -11,12 +11,14 @@ import com.coffee.common.result.R;
 import com.coffee.common.result.ResultCode;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Profile;
+import org.springframework.http.HttpMethod;
 import org.springframework.web.servlet.HandlerInterceptor;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.nio.charset.Charset;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @author lifang
@@ -33,13 +35,21 @@ public class CheckTimeHandler implements HandlerInterceptor {
     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
         String method = request.getMethod();
         Long timestamp = Value.simple(request.getHeader("Timestamp")).asLong();
-        if(timestamp==null){
+        if(timestamp==0L){
             response.getOutputStream().write(JSONUtil.parse(R.result(ResultCode.PARAM_MISS,"请检查是否携带Timestamp、Sign、Authorization")).toString().getBytes());
         }
-        if (method.equals("POST")) {
+        if (method.equals(HttpMethod.POST.name())) {
             //post 请求验证参数
             String sign = request.getHeader("Sign");
-//            redisUtils.get()
+            Object result = redisUtils.get(sign);
+            if(result==null){
+                //请求30秒过期
+                redisUtils.expire(sign, 30);
+                return true;
+            }else {
+                response.getOutputStream().write(JSONUtil.parse(R.result(ResultCode.REQUEST_EXPIRE,"该请求已过期,请刷新页面后重试")).toString().getBytes());
+                return false;
+            }
         }
         //get请求
         return true;

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

@@ -230,8 +230,8 @@ public class DeviceInfoListener {
             device.setMaster(true);
         }
         if (isNewInFusion(device.getDeviceId(),device.getClassification())) {
-            BusInfusionHistoryEntity exist=infusionHistoryService.findInfusion(device.getDeviceId(),device.getClassification());
-            if(exist==null){
+//            BusInfusionHistoryEntity exist=infusionHistoryService.findInfusion(device.getDeviceId(),device.getClassification());
+//            if(exist==null){
                 infusionHistory.setId(IdWorker.getIdStr());
                 infusionHistory.setFinished(false);
                 infusionHistory.setStartTime(device.getUploadTime());
@@ -242,11 +242,11 @@ public class DeviceInfoListener {
                         .eq(BusInfusionHistoryEntity::getTenantId,device.getTenantId())
                         .eq(BusInfusionHistoryEntity::getFinished,false)
                         .set(BusInfusionHistoryEntity::getFinished,true));
-            }else {
-                device.setNewInfusion(false);
-                infusionHistory.setStartTime(device.getStartTime());
-                infusionHistory.setId(exist.getId());
-            }
+//            }else {
+//                device.setNewInfusion(false);
+//                infusionHistory.setStartTime(device.getStartTime());
+//                infusionHistory.setId(exist.getId());
+//            }
         }else {
             device.setNewInfusion(false);
             infusionHistory.setStartTime(device.getStartTime());

+ 188 - 0
coffee-system/src/main/resources/mapper/bus/BusInfusionHistoryMapper.xml

@@ -82,6 +82,34 @@
         <result column="warn_flow" property="warnFlow"/>
     </resultMap>
 
+    <resultMap id="combineEvalResult" type="com.coffee.bus.service.dto.CombineEvalResult">
+        <result column="infusion_id" property="id"/>
+        <result column="eval_id" property="evalId"/>
+        <result column="infusion_start_time" property="infusionStartTime"/>
+        <result column="statics" property="statics"/>
+        <result column="activity" property="activity"/>
+        <result column="calm" property="calm"/>
+        <result column="left_arm" property="leftArm"/>
+        <result column="left_leg" property="leftLeg"/>
+        <result column="right_arm" property="rightArm"/>
+        <result column="right_leg" property="rightLeg"/>
+        <result column="nausea_vomit" property="nauseaVomit"/>
+        <result column="itch" property="itch"/>
+        <result column="vertigo" property="vertigo"/>
+        <result column="sore_throat" property="soreThroat"/>
+        <result column="uroschesis" property="uroschesis"/>
+        <result column="breath_depression" property="breathDepression"/>
+        <result column="hoarseness" property="hoarseness"/>
+        <result column="cognition_obstacle" property="cognitionObstacle"/>
+        <result column="satisfaction" property="satisfaction"/>
+        <result column="shrink_pressure" property="shrinkPressure"/>
+        <result column="diastolic_pressure" property="diastolicPressure"/>
+        <result column="heart_rate" property="heartRate"/>
+        <result column="fetal_heart_rate" property="fetalHeartRate"/>
+        <result column="breath_rate" property="breathRate"/>
+        <result column="blood_oxygen_saturation" property="bloodOxygenSaturation"/>
+    </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
@@ -578,4 +606,164 @@
         ) as c
         on i.clinic_id=c.id
     </select>
+
+
+    <select id="queryStatsEval" resultMap="combineEvalResult">
+        select
+        i.id as infusion_id,
+        i.start_time as infusion_start_time,
+        e.statics as statics,
+        e.activity as activity,
+        e.calm as calm,
+        e.left_arm as left_arm,
+        e.left_leg as left_leg,
+        e.right_arm as right_arm,
+        e.right_leg as right_leg,
+        e.nausea_vomit as nausea_vomit,
+        e.itch as itch,
+        e.vertigo as vertigo,
+        e.sore_throat as sore_throat,
+        e.uroschesis as uroschesis,
+        e.breath_depression as breath_depression,
+        e.hoarseness as hoarseness,
+        e.cognition_obstacle as cognition_obstacle,
+        e.satisfaction as satisfaction,
+        e.shrink_pressure as shrink_pressure,
+        e.diastolic_pressure as diastolic_pressure,
+        e.heart_rate as heart_rate,
+        e.fetal_heart_rate as fetal_heart_rate,
+        e.breath_rate as breath_rate,
+        e.blood_oxygen_saturation as blood_oxygen_saturation,
+        e.id as eval_id
+        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
+        left join (select * from bus_evaluation) as e
+        on e.infusion_id=i.id
+    </select>
 </mapper>