浏览代码

重构采集和预测逻辑,增加模型异步调用

欧阳劲驰 1 月之前
父节点
当前提交
e415296cf5
共有 64 个文件被更改,包括 4458 次插入202 次删除
  1. 14 0
      pom.xml
  2. 12 6
      sql.properties
  3. 81 0
      src/main/java/com/shkpr/service/aimodelpower/bizmgr/PredictBizMgr.java
  4. 52 0
      src/main/java/com/shkpr/service/aimodelpower/bizmgr/WaterPumpBizMgr.java
  5. 71 0
      src/main/java/com/shkpr/service/aimodelpower/bizmgr/WaterVolumeBizMgr.java
  6. 49 1
      src/main/java/com/shkpr/service/aimodelpower/commproperties/CollectProperties.java
  7. 43 0
      src/main/java/com/shkpr/service/aimodelpower/commproperties/PredictProperties.java
  8. 164 0
      src/main/java/com/shkpr/service/aimodelpower/commtools/AsyncRequestUtil.java
  9. 126 0
      src/main/java/com/shkpr/service/aimodelpower/commtools/ModelPredictorUtil.java
  10. 442 0
      src/main/java/com/shkpr/service/aimodelpower/components/ModelPredictor.java
  11. 226 0
      src/main/java/com/shkpr/service/aimodelpower/components/WaterPumpCollector.java
  12. 516 0
      src/main/java/com/shkpr/service/aimodelpower/components/WaterVolumeCollector.java
  13. 5 5
      src/main/java/com/shkpr/service/aimodelpower/configuration/ChildPgDataSourceConfig.java
  14. 89 0
      src/main/java/com/shkpr/service/aimodelpower/configuration/OracleDataSourceConfig.java
  15. 57 15
      src/main/java/com/shkpr/service/aimodelpower/configuration/ScheduleTaskConfiguration.java
  16. 1 1
      src/main/java/com/shkpr/service/aimodelpower/configuration/SecondSourceConfiguration.java
  17. 3 0
      src/main/java/com/shkpr/service/aimodelpower/constants/LogFlagBusiType.java
  18. 30 0
      src/main/java/com/shkpr/service/aimodelpower/constants/ModelPredictorTaskStatus.java
  19. 22 0
      src/main/java/com/shkpr/service/aimodelpower/constants/WaterPumpStandard.java
  20. 0 67
      src/main/java/com/shkpr/service/aimodelpower/dbdao/mapper2th/PatrolObjectAttMapper.java
  21. 85 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperoracle/ShizilaishuiHistoryMapper.java
  22. 22 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperwatervolume/OrgConfigMapper.java
  23. 30 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperwatervolume/WaterPumpCollectionConfigMapper.java
  24. 34 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperwatervolume/WaterPumpCollectionRecordMapper.java
  25. 22 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperwatervolume/WaterVolumeCollectionConfigMapper.java
  26. 66 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperwatervolume/WaterVolumeCollectionRecordMapper.java
  27. 43 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperwatervolume/WaterVolumePredictHourMapper.java
  28. 34 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/OrgConfigServiceImpl.java
  29. 207 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/ShizilaishuiHistoryServiceImpl.java
  30. 46 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/WaterPumpCollectionConfigServiceImpl.java
  31. 160 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/WaterPumpCollectionRecordServiceImpl.java
  32. 34 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/WaterVolumeCollectionConfigServiceImpl.java
  33. 249 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/WaterVolumeCollectionRecordServiceImpl.java
  34. 166 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/WaterVolumePredictHourServiceImpl.java
  35. 20 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/OrgConfigService.java
  36. 64 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/ShizilaishuiHistoryService.java
  37. 27 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/WaterPumpCollectionConfigService.java
  38. 32 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/WaterPumpCollectionRecordService.java
  39. 20 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/WaterVolumeCollectionConfigService.java
  40. 61 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/WaterVolumeCollectionRecordService.java
  41. 41 0
      src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/WaterVolumePredictHourService.java
  42. 35 0
      src/main/java/com/shkpr/service/aimodelpower/dto/ModelPredictorResult.java
  43. 45 0
      src/main/java/com/shkpr/service/aimodelpower/dto/ModelPredictorTask.java
  44. 25 0
      src/main/java/com/shkpr/service/aimodelpower/dto/OrgConfig.java
  45. 33 0
      src/main/java/com/shkpr/service/aimodelpower/dto/ShizilaishuiHistory.java
  46. 61 0
      src/main/java/com/shkpr/service/aimodelpower/dto/WaterPumpCollectionConfig.java
  47. 41 0
      src/main/java/com/shkpr/service/aimodelpower/dto/WaterPumpCollectionRecord.java
  48. 49 0
      src/main/java/com/shkpr/service/aimodelpower/dto/WaterPumpEnergyConfig.java
  49. 41 0
      src/main/java/com/shkpr/service/aimodelpower/dto/WaterVolumeCollectionConfig.java
  50. 56 0
      src/main/java/com/shkpr/service/aimodelpower/dto/WaterVolumeCollectionRecord.java
  51. 69 0
      src/main/java/com/shkpr/service/aimodelpower/dto/WaterVolumePredictHour.java
  52. 67 70
      src/main/java/com/shkpr/service/aimodelpower/globalmgr/ScheduleTaskMgr.java
  53. 29 0
      src/main/java/com/shkpr/service/aimodelpower/jsonbean/ModelPredictorDay.java
  54. 25 0
      src/main/java/com/shkpr/service/aimodelpower/jsonbean/ModelPredictorHour.java
  55. 21 0
      src/main/java/com/shkpr/service/aimodelpower/jsonbean/ModelPredictorTrain.java
  56. 55 36
      src/main/resources/application.properties
  57. 8 0
      src/main/resources/mapper/OrgConfigMapper.xml
  58. 129 0
      src/main/resources/mapper/ShizilaishuiHistoryMapper.xml
  59. 28 0
      src/main/resources/mapper/WaterPumpCollectionConfigMapper.xml
  60. 24 0
      src/main/resources/mapper/WaterPumpCollectionRecordMapper.xml
  61. 8 0
      src/main/resources/mapper/WaterVolumeCollectionConfigMapper.xml
  62. 57 0
      src/main/resources/mapper/WaterVolumeCollectionRecordMapper.xml
  63. 73 0
      src/main/resources/mapper/WaterVolumePredictHourMapper.xml
  64. 13 1
      src/test/java/com/shkpr/service/aimodelpower/AimodelpowerApplicationTests.java

+ 14 - 0
pom.xml

@@ -270,6 +270,20 @@
 			<artifactId>ojdbc8</artifactId>
 			<version>19.3.0.0</version>
 		</dependency>
+		<dependency>
+			<groupId>com.oracle.database.nls</groupId>
+			<artifactId>orai18n</artifactId>
+			<version>19.3.0.0</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>fluent-hc</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.apache.httpcomponents</groupId>
+			<artifactId>httpclient</artifactId>
+		</dependency>
 	</dependencies>
 
 	<build>

+ 12 - 6
sql.properties

@@ -4,7 +4,13 @@ spring.datasource.data.username=postgres
 spring.datasource.data.password=kpr.23417.postgres
 spring.datasource.data.driver-class-name=org.postgis.DriverWrapper
 
-#pgsql 水量预测数据库源
+#oracle
+spring.datasource.oracle.jdbc-url=jdbc:oracle:thin:@10.127.16.117:1521/ORCLPDB1
+spring.datasource.oracle.username=v_cqzls
+spring.datasource.oracle.password=CQzls#2025
+spring.datasource.oracle.driver-class-name=oracle.jdbc.driver.OracleDriver
+
+#pgsql 水锟斤拷预锟斤拷锟斤拷锟捷匡拷源
 #spring.datasource.db2.jdbc-url=jdbc:postgresql_postGIS://140.246.183.164:5432/water_volume_prediction?useSSL=false&useAffectedRows=false&allowMultiQueries=true
 #spring.datasource.db2.username=postgres
 #spring.datasource.db2.password=kpr.23417.postgres
@@ -17,7 +23,7 @@ spring.datasource.db2.driver-class-name=org.postgis.DriverWrapper
 #spring,database.name=water_volume_prediction_jiangjin2
 spring,database.name=water_volume_prediction
 
-spring.datasource.child2pg.jdbc-url=jdbc:postgresql_postGIS://10.101.5.50:5432/water_level_prediction?useSSL=false&useAffectedRows=false&allowMultiQueries=true
+spring.datasource.child2pg.jdbc-url=jdbc:postgresql_postGIS://10.101.5.50:5432/water_level_prediction?useSSL=false&useAffectedRows=false&allowMultiQueries=true&rewriteBatchedStatements=true
 spring.datasource.child2pg.username=postgres
 spring.datasource.child2pg.password=ygt.23417.postgres
 spring.datasource.child2pg.driver-class-name=org.postgis.DriverWrapper
@@ -39,9 +45,9 @@ spring.datasource.data2.test-while-idle=true
 spring.datasource.data2.max-lifetime=60000
 spring.datasource.data2.idle-timeout=30000
 spring.datasource.data2.connection-timeout=30000
-# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+# 锟斤拷锟矫硷拷锟斤拷锟矫才斤拷锟斤拷一锟轿硷拷猓�拷锟斤拷锟斤拷要锟截闭的匡拷锟斤拷锟斤拷锟接o拷锟斤拷位锟角猴拷锟斤拷
 spring.datasource.data2.time-between-eviction-runs-millis=60000
-# 配置一个连接在池中最小生存的时间,单位是毫秒
+# ����һ�������ڳ�����С�����ʱ�䣬��λ�Ǻ���
 spring.datasource.data2.minEvictableIdleTimeMillis=300000
 
 spring.datasource.data.max-wait=60000
@@ -55,7 +61,7 @@ spring.datasource.data.test-while-idle=true
 spring.datasource.data.max-lifetime=120000
 spring.datasource.data.idle-timeout=30000
 spring.datasource.data.connection-timeout=30000
-# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+# 锟斤拷锟矫硷拷锟斤拷锟矫才斤拷锟斤拷一锟轿硷拷猓�拷锟斤拷锟斤拷要锟截闭的匡拷锟斤拷锟斤拷锟接o拷锟斤拷位锟角猴拷锟斤拷
 spring.datasource.data.time-between-eviction-runs-millis=60000
-# 配置一个连接在池中最小生存的时间,单位是毫秒
+# ����һ�������ڳ�����С�����ʱ�䣬��λ�Ǻ���
 spring.datasource.data.minEvictableIdleTimeMillis=300000

+ 81 - 0
src/main/java/com/shkpr/service/aimodelpower/bizmgr/PredictBizMgr.java

@@ -0,0 +1,81 @@
+package com.shkpr.service.aimodelpower.bizmgr;
+
+import com.shkpr.service.aimodelpower.components.ModelPredictor;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * 预测管理
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Component
+public class PredictBizMgr {
+    final
+    ThreadPoolTaskExecutor taskScheduler;
+    final
+    ModelPredictor modelPredictor;
+
+    public PredictBizMgr(@Qualifier("taskExecutor") ThreadPoolTaskExecutor taskScheduler, ModelPredictor modelPredictor) {
+        this.taskScheduler = taskScheduler;
+        this.modelPredictor = modelPredictor;
+    }
+
+    /**
+     * 初始化
+     */
+    @PostConstruct
+    public void init() {
+        taskScheduler.execute(() -> {
+            //预测水量
+            modelPredictor.predictDayWater(2);
+            modelPredictor.predictHourWater(2);
+            modelPredictor.predictMinuteWater(2);
+            //预测小时泵,同步小时泵
+            modelPredictor.predictHourPump(2);
+            modelPredictor.syncHourPump(2);
+        });
+    }
+
+    /**
+     * 分钟任务
+     */
+    @Scheduled(cron = "0 */10 * * * *")
+    public void minuteTask() {
+        //预测分钟水量
+        taskScheduler.execute(() -> modelPredictor.predictMinuteWater(2));
+    }
+
+    /**
+     * 小时任务
+     */
+    @Scheduled(cron = "0 15 * * * *")
+    public void hourTask() {
+        taskScheduler.execute(() -> {
+            //预测小时水量
+            modelPredictor.predictHourWater(2);
+            //预测小时泵,同步小时泵
+            modelPredictor.predictHourPump(2);
+            modelPredictor.syncHourPump(2);
+        });
+    }
+
+    /**
+     * 日任务
+     */
+    @Scheduled(cron = "0 25 0 * * ?")
+    public void dayTask() {
+        taskScheduler.execute(() -> {
+            //预测日水量
+            modelPredictor.predictDayWater(2);
+            //训练水量
+            modelPredictor.trainDayWater();
+            modelPredictor.trainHourWater();
+        });
+    }
+}

+ 52 - 0
src/main/java/com/shkpr/service/aimodelpower/bizmgr/WaterPumpBizMgr.java

@@ -0,0 +1,52 @@
+package com.shkpr.service.aimodelpower.bizmgr;
+
+import com.shkpr.service.aimodelpower.components.WaterPumpCollector;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * 泵数据管理
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Component
+public class WaterPumpBizMgr {
+    final
+    ThreadPoolTaskExecutor taskScheduler;
+    final
+    WaterPumpCollector waterPumpCollector;
+
+    public WaterPumpBizMgr(@Qualifier("taskExecutor") ThreadPoolTaskExecutor taskScheduler, WaterPumpCollector waterPumpCollector) {
+        this.taskScheduler = taskScheduler;
+        this.waterPumpCollector = waterPumpCollector;
+    }
+
+    /**
+     * 初始化
+     */
+    @PostConstruct
+    public void init() {
+        //采集4天数据
+        taskScheduler.execute(() -> waterPumpCollector.collectData(4 * 24));
+        taskScheduler.execute(() -> waterPumpCollector.calcPower(4 * 24));
+    }
+
+    /**
+     * 小时任务
+     */
+    @Scheduled(cron = "0 5 * * * *")
+    public void hourTask() {
+        taskScheduler.execute(() -> {
+            //采集2小时的数据
+            waterPumpCollector.collectData(2);
+            //计算2小时用电量
+            waterPumpCollector.calcPower(2);
+        });
+    }
+
+}

+ 71 - 0
src/main/java/com/shkpr/service/aimodelpower/bizmgr/WaterVolumeBizMgr.java

@@ -0,0 +1,71 @@
+package com.shkpr.service.aimodelpower.bizmgr;
+
+import com.shkpr.service.aimodelpower.components.WaterVolumeCollector;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * 水量管理
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Component
+public class WaterVolumeBizMgr {
+    final
+    ThreadPoolTaskExecutor taskScheduler;
+    final
+    WaterVolumeCollector waterVolumeCollector;
+
+    public WaterVolumeBizMgr(@Qualifier("taskExecutor") ThreadPoolTaskExecutor taskScheduler, WaterVolumeCollector waterVolumeCollector) {
+        this.taskScheduler = taskScheduler;
+        this.waterVolumeCollector = waterVolumeCollector;
+    }
+
+    /**
+     * 初始化
+     */
+    @PostConstruct
+    public void init() {
+        //采集4天数据
+        taskScheduler.execute(() -> waterVolumeCollector.collectBusinessMinuteData(4 * 24));
+        taskScheduler.execute(() -> waterVolumeCollector.collectDifferenceMinuteData(4 * 24));
+        taskScheduler.execute(() -> waterVolumeCollector.collectBusinessHourData(4 * 24));
+        taskScheduler.execute(() -> waterVolumeCollector.collectDifferenceHourData(4 * 24));
+    }
+
+    /**
+     * 分钟任务
+     */
+    @Scheduled(cron = "0 */10 * * * *")
+    public void minuteTask() {
+        //采集营业所1小时的分钟数据
+        taskScheduler.execute(() -> waterVolumeCollector.collectBusinessMinuteData(1));
+        //采集1小时的分钟数据
+        taskScheduler.execute(() -> waterVolumeCollector.collectDifferenceMinuteData(1));
+    }
+
+    /**
+     * 小时任务
+     */
+    @Scheduled(cron = "0 5 * * * *")
+    public void hourTask() {
+        //采集营业所2小时的小时数据
+        taskScheduler.execute(() -> waterVolumeCollector.collectBusinessHourData(2));
+        //采集2小时的小时数据
+        taskScheduler.execute(() -> waterVolumeCollector.collectDifferenceHourData(2));
+    }
+
+    /**
+     * 日任务
+     */
+    @Scheduled(cron = "0 5 0 * * ?")
+    public void dayTask() {
+        //采集次日的原始数据
+        taskScheduler.execute(() -> waterVolumeCollector.collectRawData(24));
+    }
+}

+ 49 - 1
src/main/java/com/shkpr/service/aimodelpower/commproperties/CollectProperties.java

@@ -6,11 +6,15 @@ import lombok.ToString;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Configuration;
 
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
 /**
  * 采集属性
  *
  * @author 欧阳劲驰
- * @since 1.0.3
+ * @since 1.0.4
  */
 @ConfigurationProperties("collect")
 @Configuration
@@ -26,4 +30,48 @@ public class CollectProperties {
      * 最小水量
      */
     private Double minWater = -20000d;
+    /**
+     * 关闭标签
+     */
+    private List<String> closeTags;
+    /**
+     * 自供标签
+     */
+    private List<CollectTagAttr> supplySelfTags;
+    /**
+     * 供入标签
+     */
+    private List<CollectTagAttr> supplyInTags;
+    /**
+     * 供出标签
+     */
+    private List<CollectTagAttr> supplyOutTags;
+
+    public Map<String, List<String>> getSupplySelfTagsMap() {
+        return supplySelfTags.stream().collect(Collectors.toMap(CollectProperties.CollectTagAttr::getName, CollectProperties.CollectTagAttr::getValues));
+    }
+
+    public Map<String, List<String>> getSupplyInTagsMap() {
+        return supplyInTags.stream().collect(Collectors.toMap(CollectProperties.CollectTagAttr::getName, CollectProperties.CollectTagAttr::getValues));
+    }
+
+    public Map<String, List<String>> getSupplyOutTagsMap() {
+        return supplyOutTags.stream().collect(Collectors.toMap(CollectProperties.CollectTagAttr::getName, CollectProperties.CollectTagAttr::getValues));
+    }
+
+    /**
+     * 采集标签
+     */
+    @Getter
+    @Setter
+    public static class CollectTagAttr {
+        /**
+         * 名称
+         */
+        private String name;
+        /**
+         * 值
+         */
+        private List<String> values;
+    }
 }

+ 43 - 0
src/main/java/com/shkpr/service/aimodelpower/commproperties/PredictProperties.java

@@ -0,0 +1,43 @@
+package com.shkpr.service.aimodelpower.commproperties;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+/**
+ * 预测属性
+ *
+ * @author 欧阳劲驰
+ * @since 1.0.4
+ */
+@ConfigurationProperties("predict")
+@Configuration
+@Getter
+@Setter
+@ToString
+public class PredictProperties {
+    /**
+     * 预测地址
+     */
+    private String url;
+    /**
+     * 连接超时(毫秒)
+     */
+    private Integer connectTimeout = 30 * 100;
+    /**
+     * 读取超时(毫秒)
+     */
+    private Integer readTimeout = 30 * 100;
+    /**
+     * 关闭的组织
+     */
+    private List<String> closeOrgNames;
+    /**
+     * 重试次数
+     */
+    private Integer retryCount = 10;
+}

+ 164 - 0
src/main/java/com/shkpr/service/aimodelpower/commtools/AsyncRequestUtil.java

@@ -0,0 +1,164 @@
+package com.shkpr.service.aimodelpower.commtools;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.aimodelpower.commproperties.PredictProperties;
+import com.shkpr.service.aimodelpower.constants.LogFlagBusiType;
+import com.shkpr.service.aimodelpower.constants.ModelPredictorTaskStatus;
+import com.shkpr.service.aimodelpower.dto.ModelPredictorResult;
+import com.shkpr.service.aimodelpower.dto.ModelPredictorTask;
+import org.apache.commons.lang3.StringEscapeUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.fluent.ContentResponseHandler;
+import org.apache.http.client.fluent.Request;
+import org.apache.http.client.fluent.Response;
+import org.apache.http.entity.ContentType;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.Objects;
+import java.util.concurrent.*;
+import java.util.stream.IntStream;
+
+@Component
+public class AsyncRequestUtil {
+    /**
+     * log
+     */
+    private final static String mStrClassName = "ModelPredictor";
+    private final static String mBizType = LogFlagBusiType.BUSI_ALL.toStrValue();
+    //线程池
+    private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
+
+    final
+    PredictProperties predictProperties;
+    final
+    ObjectMapper objectMapper;
+
+    public AsyncRequestUtil(PredictProperties predictProperties, ObjectMapper objectMapper) {
+        this.predictProperties = predictProperties;
+        this.objectMapper = objectMapper;
+    }
+
+    /**
+     * 开始异步
+     *
+     * @param url  地址
+     * @param body 参数
+     * @return 请求结果
+     */
+    public ModelPredictorResult start(String url, Object body) {
+        //结果任务
+        CompletableFuture<ModelPredictorResult> future = new CompletableFuture<>();
+        //循环执行
+        IntStream.range(0, predictProperties.getRetryCount()).forEach(i -> scheduler.schedule(() -> {
+            try {
+                //检查结束
+                if (future.isDone() || future.isCancelled() || future.isCompletedExceptionally()
+                        || Thread.currentThread().isInterrupted()) return;
+
+                //发起请求
+                Response response = Request.Post(url)
+                        .bodyString(objectMapper.writeValueAsString(body), ContentType.APPLICATION_JSON)
+                        .connectTimeout(predictProperties.getConnectTimeout())
+                        .socketTimeout(predictProperties.getReadTimeout())
+                        .execute();
+
+                //获取返回
+                HttpResponse httpResponse = response.returnResponse();
+                //获取内容
+                String content = StringEscapeUtils.unescapeJava(new ContentResponseHandler().handleResponse(httpResponse).toString());
+                //解析内容
+                ModelPredictorResult result = objectMapper.readValue(content, ModelPredictorResult.class);
+
+                //判断结果
+                if (Objects.equals(Boolean.TRUE.toString(), result.getResult())) future.complete(result);
+                else LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                        , String.format("异步调用失败, url:%s, body:%s, msg:%s", url, body, StringEscapeUtils.unescapeJava(content)));
+
+            } catch (IOException e) {
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                        , String.format("异步调用异常, url:%s, body:%s, error:%s", url, body, e)
+                );
+            }
+        }, i, TimeUnit.SECONDS));
+
+        //等待结果
+        try {
+            return future.get(predictProperties.getRetryCount(), TimeUnit.SECONDS);
+        } catch (InterruptedException | ExecutionException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                    , String.format("异步执行异常, url:%s, body:%s, error:%s", url, body, e));
+        } catch (TimeoutException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                    , String.format("异步调用重试次数过多, url:%s, body:%s", url, body));
+        }
+
+        return null;
+    }
+
+    /**
+     * 等待结果
+     *
+     * @param url 地址
+     * @return 返回key
+     */
+    public ModelPredictorResult wait(String url) {
+        //结果任务
+        CompletableFuture<ModelPredictorResult> future = new CompletableFuture<>();
+        //循环执行
+        IntStream.range(0, predictProperties.getRetryCount()).forEach(i -> scheduler.schedule(() -> {
+            try {
+                //检查结束
+                if (future.isDone() || Thread.currentThread().isInterrupted()) return;
+
+                //发起请求
+                Response response = Request.Get(url)
+                        .connectTimeout(predictProperties.getConnectTimeout())
+                        .socketTimeout(predictProperties.getReadTimeout())
+                        .execute();
+
+                //获取返回
+                HttpResponse httpResponse = response.returnResponse();
+                //获取内容
+                String content = StringEscapeUtils.unescapeJava(new ContentResponseHandler().handleResponse(httpResponse).toString());
+                //解析内容
+                ModelPredictorResult result = objectMapper.readValue(content, ModelPredictorResult.class);
+
+                //判断结果
+                if (Objects.equals(Boolean.TRUE.toString(), result.getResult())) {
+                    //解析任务
+                    ModelPredictorTask task = objectMapper.readValue(objectMapper.writeValueAsString(result.getKeyValue())
+                            , ModelPredictorTask.class);
+                    result.setTask(task);
+                    //判断任务状态
+                    if (StringUtils.equalsAny(task.getStatus(), ModelPredictorTaskStatus.COMPLETED
+                            , ModelPredictorTaskStatus.FAILED, ModelPredictorTaskStatus.CANCELLED)) {
+                        future.complete(result);
+                    }
+                } else LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                        , String.format("异步调用失败, url:%s, msg:%s", url, content));
+
+            } catch (IOException e) {
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                        , String.format("异步调用异常, url:%s, error:%s", url, e)
+                );
+            }
+        }, i, TimeUnit.SECONDS));
+
+        //等待结果
+        try {
+            return future.get(predictProperties.getRetryCount(), TimeUnit.SECONDS);
+        } catch (InterruptedException | ExecutionException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                    , String.format("异步执行异常, url:%s, error:%s", url, e));
+        } catch (TimeoutException e) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                    , String.format("异步调用重试次数过多, url:%s", url));
+        }
+
+        return null;
+    }
+}

+ 126 - 0
src/main/java/com/shkpr/service/aimodelpower/commtools/ModelPredictorUtil.java

@@ -0,0 +1,126 @@
+package com.shkpr.service.aimodelpower.commtools;
+
+import com.shkpr.service.aimodelpower.dto.WaterVolumePredictHour;
+import org.springframework.beans.BeanUtils;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 模型预测工具类
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+public class ModelPredictorUtil {
+    /**
+     * 计算分钟水量
+     *
+     * @param hours 小时数据
+     * @return 分钟数据
+     */
+    public static List<WaterVolumePredictHour> calcMinuteWater(List<WaterVolumePredictHour> hours) {
+        List<List<WaterVolumePredictHour>> trendGroups = new ArrayList<>();
+        List<WaterVolumePredictHour> currentGroup = new ArrayList<>();
+
+        List<WaterVolumePredictHour> minutes = new ArrayList<>();
+
+        for (int i = 0; i < hours.size(); i++) {
+            WaterVolumePredictHour current = hours.get(i);
+
+            double currentHourData = current.getHourForecastActualWaterSupply();
+
+            if (i == 0) {
+                currentGroup.add(current);
+            } else {
+                WaterVolumePredictHour previous = hours.get(i - 1);
+                double previousHourData = previous.getHourForecastActualWaterSupply();
+
+                boolean isIncreasing = currentHourData > previousHourData;
+                if (!currentGroup.isEmpty()) {
+                    WaterVolumePredictHour lastInGroup = currentGroup.get(currentGroup.size() - 1);
+                    double lastGroupData = lastInGroup.getHourForecastActualWaterSupply();
+                    boolean wasIncreasing = lastGroupData < currentHourData;
+
+                    if (isIncreasing != wasIncreasing) {
+                        trendGroups.add(new ArrayList<>(currentGroup));
+                        currentGroup.clear();
+                    }
+                }
+                currentGroup.add(current);
+            }
+        }
+
+        if (!currentGroup.isEmpty()) {
+            trendGroups.add(currentGroup);
+        }
+
+        for (List<WaterVolumePredictHour> group : trendGroups) {
+            for (int i = 0; i < group.size() - 1; i++) {
+                WaterVolumePredictHour current = group.get(i);
+                double currentHourData = current.getHourForecastActualWaterSupply();
+                double nextHourData = group.get(i + 1).getHourForecastActualWaterSupply();
+
+                boolean isIncreasing = nextHourData > currentHourData;
+                double[] splitData = splitHourData(currentHourData, isIncreasing);
+
+                // 平滑处理
+                double alpha = 0.3; // 平滑系数
+                double[] smoothedData = exponentialSmoothing(splitData, alpha);
+
+                LocalDateTime dateTime = LocalDateTime.of(current.getDate(), current.getHour())
+                        .minusMinutes(45);
+
+                for (int j = 0; j < smoothedData.length; j++) {
+                    WaterVolumePredictHour newHourWater = new WaterVolumePredictHour();
+                    // 复制属性
+                    BeanUtils.copyProperties(current, newHourWater);
+
+                    newHourWater.setHourForecastActualWaterSupply(smoothedData[j]);
+                    LocalDateTime quarterHourTime = dateTime.plusMinutes(j * 15L);
+                    newHourWater.setDate(quarterHourTime.toLocalDate());
+                    newHourWater.setHour(quarterHourTime.toLocalTime());
+
+                    // 查询原始值并设置
+                    double originalValue = 100;
+                    newHourWater.setHourActualWaterSupply(originalValue);
+                    newHourWater.setLastModifyTime(LocalDateTime.now());
+
+                    minutes.add(newHourWater);
+                }
+            }
+        }
+
+        return minutes;
+    }
+
+    //拆分函数
+    private static double[] splitHourData(double hourData, boolean isIncreasing) {
+        double[] splitData = new double[4];
+        if (isIncreasing) {
+            // 调整比例以实现更平滑的上升曲线
+            splitData[0] = hourData * 0.24;
+            splitData[1] = hourData * 0.26;
+            splitData[2] = hourData * 0.25;
+            splitData[3] = hourData * 0.25;
+        } else {
+            // 调整比例以实现更平滑的下降曲线
+            splitData[0] = hourData * 0.25;
+            splitData[1] = hourData * 0.25;
+            splitData[2] = hourData * 0.26;
+            splitData[3] = hourData * 0.24;
+        }
+        return splitData;
+    }
+
+    //指数平滑
+    private static double[] exponentialSmoothing(double[] data, double alpha) {
+        double[] smoothedData = new double[data.length];
+        smoothedData[0] = data[0]; // 初始化第一个值
+        for (int i = 1; i < data.length; i++) {
+            smoothedData[i] = alpha * data[i] + (1 - alpha) * smoothedData[i - 1];
+        }
+        return smoothedData;
+    }
+}

+ 442 - 0
src/main/java/com/shkpr/service/aimodelpower/components/ModelPredictor.java

@@ -0,0 +1,442 @@
+package com.shkpr.service.aimodelpower.components;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.aimodelpower.commproperties.PredictProperties;
+import com.shkpr.service.aimodelpower.commtools.AsyncRequestUtil;
+import com.shkpr.service.aimodelpower.commtools.ModelPredictorUtil;
+import com.shkpr.service.aimodelpower.constants.LogFlagBusiType;
+import com.shkpr.service.aimodelpower.constants.ModelPredictorTaskStatus;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.OrgConfigService;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.WaterVolumePredictHourService;
+import com.shkpr.service.aimodelpower.dto.ModelPredictorResult;
+import com.shkpr.service.aimodelpower.dto.ModelPredictorTask;
+import com.shkpr.service.aimodelpower.dto.OrgConfig;
+import com.shkpr.service.aimodelpower.dto.WaterVolumePredictHour;
+import com.shkpr.service.aimodelpower.jsonbean.ModelPredictorDay;
+import com.shkpr.service.aimodelpower.jsonbean.ModelPredictorHour;
+import com.shkpr.service.aimodelpower.jsonbean.ModelPredictorTrain;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 模型预测器
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Component
+public class ModelPredictor {
+    /**
+     * log
+     */
+    private final static String mStrClassName = "ModelPredictor";
+    private final static String mBizType = LogFlagBusiType.BUSI_MODEL_PREDICT.toStrValue();
+    /**
+     * 预测日水量路径
+     */
+    private final static String predictDayWaterUri = "PredictSupplyAsync";
+    /**
+     * 预测小时水量路径
+     */
+    private final static String predictHourWaterUri = "PredictHourAsync";
+    /**
+     * 训练日水量路径
+     */
+    private final static String trainDayWaterUri = "TrainSupplyAsync";
+    /**
+     * 训练小时水量路径
+     */
+    private final static String trainHourWaterUri = "TrainHourSupplyAsync";
+    /**
+     * 预测小时泵路径
+     */
+    private final static String predictHourPumpUri = "PumpSchedulingAsync";
+    /**
+     *
+     */
+    private final static String taskProgressUri = "task/progress/";
+
+    final
+    ThreadPoolTaskExecutor taskScheduler;
+    final
+    ObjectMapper objectMapper;
+    final
+    PredictProperties predictProperties;
+    final
+    OrgConfigService orgConfigService;
+    final
+    WaterVolumePredictHourService waterVolumePredictHourService;
+    final
+    AsyncRequestUtil asyncRequestUtil;
+
+    public ModelPredictor(@Qualifier("taskExecutor") ThreadPoolTaskExecutor taskScheduler, ObjectMapper objectMapper
+            , PredictProperties predictProperties, OrgConfigService orgConfigService
+            , WaterVolumePredictHourService waterVolumePredictHourService, AsyncRequestUtil asyncRequestUtil) {
+        this.taskScheduler = taskScheduler;
+        this.objectMapper = objectMapper;
+        this.predictProperties = predictProperties;
+        this.orgConfigService = orgConfigService;
+        this.waterVolumePredictHourService = waterVolumePredictHourService;
+        this.asyncRequestUtil = asyncRequestUtil;
+    }
+
+    /**
+     * 预测日水量
+     *
+     * @param nextDays 往后预测天数
+     */
+    public void predictDayWater(int nextDays) {
+        //计算开始和结束日期
+        LocalDate startTime = LocalDate.now();
+        LocalDate endTime = LocalDate.now().plusDays(nextDays);
+
+        //关闭的组织名称
+        List<String> closeOrgNames = predictProperties.getCloseOrgNames();
+        //获取组织配置
+        List<OrgConfig> orgConfigs = orgConfigService.findAll().stream()
+                //排除关闭组织
+                .filter(config -> !closeOrgNames.contains(config.getOrgName()))
+                .collect(Collectors.toList());
+
+        //遍历组织配置
+        orgConfigs.forEach(orgConfig -> {
+            long begin = System.currentTimeMillis();
+
+            //请求参数
+            ModelPredictorDay predictorDay = new ModelPredictorDay(orgConfig.getOrgId(), startTime.toString(), endTime.toString());
+            //发送请求
+            ModelPredictorResult startResult = asyncRequestUtil.start(predictProperties.getUrl() + predictDayWaterUri, predictorDay);
+
+            //任务id
+            String taskId = (String) startResult.getKeyValue();
+            if (StringUtils.isBlank(taskId)) return;
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                    , String.format("开始执行预测日水量 组织名称:%s,开始日期:%s,结束日期:%s,任务id:%s",
+                            orgConfig.getOrgName(), startTime, endTime, taskId));
+
+            //等待结果
+            ModelPredictorResult taskResult = asyncRequestUtil.wait(predictProperties.getUrl() + taskProgressUri + taskId);
+            if (taskResult == null || taskResult.getTask() == null) return;
+
+            long end = System.currentTimeMillis();
+            //任务状态
+            ModelPredictorTask task = taskResult.getTask();
+            switch (task.getStatus()) {
+                case ModelPredictorTaskStatus.COMPLETED:
+                    LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                            , String.format("执行预测日水量成功,组织名称:%s,用时(毫秒):%d,返回:%s"
+                                    , orgConfig.getOrgName(), (end - begin), taskResult));
+                    break;
+                case ModelPredictorTaskStatus.FAILED:
+                case ModelPredictorTaskStatus.CANCELLED:
+                    LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                            , String.format("执行预测日水量失败,组织名称:%s,用时(毫秒):%d,返回:%s"
+                                    , orgConfig.getOrgName(), (end - begin), taskResult));
+                    break;
+            }
+        });
+    }
+
+    /**
+     * 预测小时水量
+     *
+     * @param nextDays 往后预测天数
+     */
+    public void predictHourWater(int nextDays) {
+        //计算开始日期
+        LocalDate startTime = LocalDate.now();
+
+        //关闭的组织名称
+        List<String> closeOrgNames = predictProperties.getCloseOrgNames();
+        //获取组织配置
+        List<OrgConfig> orgConfigs = orgConfigService.findAll().stream()
+                //排除关闭组织
+                .filter(config -> !closeOrgNames.contains(config.getOrgName()))
+                .collect(Collectors.toList());
+
+        //遍历组织配置
+        for (OrgConfig orgConfig : orgConfigs) {
+            //遍历预测天数
+            for (int i = 0; i <= nextDays; i++) {
+                long begin = System.currentTimeMillis();
+
+                //计算预测日期,开始日期+i
+                String time = startTime.plusDays(i).toString();
+                //请求参数
+                ModelPredictorHour predictorHour = new ModelPredictorHour(orgConfig.getOrgId(), time);
+
+                //发送请求
+                ModelPredictorResult startResult = asyncRequestUtil.start(predictProperties.getUrl() + predictHourWaterUri, predictorHour);
+
+                //任务id
+                String taskId = (String) startResult.getKeyValue();
+                if (StringUtils.isBlank(taskId)) return;
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format("开始执行预测小时水量 组织名称:%s,日期:%s,任务id:%s",
+                                orgConfig.getOrgName(), time, taskId));
+
+                //等待结果
+                ModelPredictorResult taskResult = asyncRequestUtil.wait(predictProperties.getUrl() + taskProgressUri + taskId);
+                if (taskResult == null || taskResult.getTask() == null) return;
+
+                long end = System.currentTimeMillis();
+                //任务状态
+                ModelPredictorTask task = taskResult.getTask();
+                switch (task.getStatus()) {
+                    case ModelPredictorTaskStatus.COMPLETED:
+                        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                                , String.format("执行预测小时水量成功,组织名称:%s,用时(毫秒):%d,返回:%s"
+                                        , orgConfig.getOrgName(), (end - begin), taskResult));
+                        break;
+                    case ModelPredictorTaskStatus.FAILED:
+                    case ModelPredictorTaskStatus.CANCELLED:
+                        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                                , String.format("执行预测小时水量失败,组织名称:%s,用时(毫秒):%d,返回:%s"
+                                        , orgConfig.getOrgName(), (end - begin), taskResult));
+                        break;
+                }
+            }
+        }
+    }
+
+    /**
+     * 预测分钟水量
+     *
+     * @param nextDays 往后预测天数
+     */
+    public void predictMinuteWater(int nextDays) {
+        //计算开始和结束日期
+        LocalDate startTime = LocalDate.now();
+        LocalDate endTime = LocalDate.now().plusDays(nextDays);
+
+        //关闭的组织名称
+        List<String> closeOrgNames = predictProperties.getCloseOrgNames();
+        //获取组织配置
+        List<OrgConfig> orgConfigs = orgConfigService.findAll().stream()
+                //排除关闭所组织
+                .filter(config -> !closeOrgNames.contains(config.getOrgName()))
+                .collect(Collectors.toList());
+
+        //遍历组织配置
+        for (OrgConfig orgConfig : orgConfigs) {
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                    , String.format("开始执行预测分钟水量 组织名称:%s,开始日期:%s,结束日期:%s",
+                            orgConfig.getOrgName(), startTime, endTime));
+            long begin = System.currentTimeMillis();
+
+            //读取小时预测数据
+            List<WaterVolumePredictHour> hours = waterVolumePredictHourService.findHour(startTime, endTime, orgConfig.getOrgId());
+            //计算分钟预测数据
+            List<WaterVolumePredictHour> minutes = ModelPredictorUtil.calcMinuteWater(hours);
+
+            //执行批量合并
+            Boolean upserted = waterVolumePredictHourService.upsertAllMinute(minutes);
+
+            long end = System.currentTimeMillis();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                    , String.format(
+                            "结束执行预测分钟水量,组织名称:%s,入库状态:%s,用时(毫秒):%d",
+                            orgConfig.getOrgName()
+                            , upserted
+                            , (end - begin)
+                    )
+            );
+        }
+
+    }
+
+    /**
+     * 训练小时水量
+     */
+    public void trainDayWater() {
+        //关闭的组织名称
+        List<String> closeOrgNames = predictProperties.getCloseOrgNames();
+        //获取组织配置
+        List<OrgConfig> orgConfigs = orgConfigService.findAll().stream()
+                //排除关闭组织
+                .filter(config -> !closeOrgNames.contains(config.getOrgName()))
+                .collect(Collectors.toList());
+
+        //遍历组织配置
+        orgConfigs.forEach(orgConfig -> {
+            long begin = System.currentTimeMillis();
+
+            //请求参数
+            ModelPredictorTrain predictorDay = new ModelPredictorTrain(orgConfig.getOrgId());
+            //发送请求
+            ModelPredictorResult startResult = asyncRequestUtil.start(predictProperties.getUrl() + trainDayWaterUri, predictorDay);
+
+            //任务id
+            String taskId = (String) startResult.getKeyValue();
+            if (StringUtils.isBlank(taskId)) return;
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                    , String.format("开始执行训练日水量 组织名称:%s,任务id:%s",
+                            orgConfig.getOrgName(), taskId));
+
+            //等待结果
+            ModelPredictorResult taskResult = asyncRequestUtil.wait(predictProperties.getUrl() + taskProgressUri + taskId);
+            if (taskResult == null || taskResult.getTask() == null) return;
+
+            long end = System.currentTimeMillis();
+            //任务状态
+            ModelPredictorTask task = taskResult.getTask();
+            switch (task.getStatus()) {
+                case ModelPredictorTaskStatus.COMPLETED:
+                    LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                            , String.format("执行训练日水量成功,组织名称:%s,用时(毫秒):%d,返回:%s"
+                                    , orgConfig.getOrgName(), (end - begin), taskResult));
+                    break;
+                case ModelPredictorTaskStatus.FAILED:
+                case ModelPredictorTaskStatus.CANCELLED:
+                    LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                            , String.format("执行训练日水量失败,组织名称:%s,用时(毫秒):%d,返回:%s"
+                                    , orgConfig.getOrgName(), (end - begin), taskResult));
+                    break;
+            }
+        });
+    }
+
+    /**
+     * 训练小时水量
+     */
+    public void trainHourWater() {
+        //关闭的组织名称
+        List<String> closeOrgNames = predictProperties.getCloseOrgNames();
+        //获取组织配置
+        List<OrgConfig> orgConfigs = orgConfigService.findAll().stream()
+                //排除关闭组织
+                .filter(config -> !closeOrgNames.contains(config.getOrgName()))
+                .collect(Collectors.toList());
+
+        //遍历组织配置
+        orgConfigs.forEach(orgConfig -> {
+            long begin = System.currentTimeMillis();
+
+            //请求参数
+            ModelPredictorTrain predictorDay = new ModelPredictorTrain(orgConfig.getOrgId());
+            //发送请求
+            ModelPredictorResult startResult = asyncRequestUtil.start(predictProperties.getUrl() + trainHourWaterUri, predictorDay);
+
+            //任务id
+            String taskId = (String) startResult.getKeyValue();
+            if (StringUtils.isBlank(taskId)) return;
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                    , String.format("开始执行训练小时水量 组织名称:%s,任务id:%s",
+                            orgConfig.getOrgName(), taskId));
+
+            //等待结果
+            ModelPredictorResult taskResult = asyncRequestUtil.wait(predictProperties.getUrl() + taskProgressUri + taskId);
+            if (taskResult == null || taskResult.getTask() == null) return;
+
+            long end = System.currentTimeMillis();
+            //任务状态
+            ModelPredictorTask task = taskResult.getTask();
+            switch (task.getStatus()) {
+                case ModelPredictorTaskStatus.COMPLETED:
+                    LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                            , String.format("执行训练小时水量成功,组织名称:%s,用时(毫秒):%d,返回:%s"
+                                    , orgConfig.getOrgName(), (end - begin), taskResult));
+                    break;
+                case ModelPredictorTaskStatus.FAILED:
+                case ModelPredictorTaskStatus.CANCELLED:
+                    LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                            , String.format("执行训练小时水量失败,组织名称:%s,用时(毫秒):%d,返回:%s"
+                                    , orgConfig.getOrgName(), (end - begin), taskResult));
+                    break;
+            }
+        });
+    }
+
+    /**
+     * 预测小时泵
+     *
+     * @param nextDays 往后预测天数
+     */
+    public void predictHourPump(int nextDays) {
+        //计算开始日期
+        LocalDate startTime = LocalDate.now();
+
+        //关闭的组织名称
+        List<String> closeOrgNames = predictProperties.getCloseOrgNames();
+        //获取组织配置
+        List<OrgConfig> orgConfigs = orgConfigService.findAll().stream()
+                //排除关闭组织
+                .filter(config -> !closeOrgNames.contains(config.getOrgName()))
+                .collect(Collectors.toList());
+
+        //遍历组织配置
+        for (OrgConfig orgConfig : orgConfigs) {
+            //遍历预测天数
+            for (int i = 0; i <= nextDays; i++) {
+                long begin = System.currentTimeMillis();
+
+                //计算预测日期,开始日期+i
+                String time = startTime.plusDays(i).toString();
+                //请求参数
+                ModelPredictorHour predictorHour = new ModelPredictorHour(orgConfig.getOrgId(), time);
+
+                //发送请求
+                ModelPredictorResult startResult = asyncRequestUtil.start(predictProperties.getUrl() + predictHourPumpUri, predictorHour);
+
+                //任务id
+                String taskId = (String) startResult.getKeyValue();
+                if (StringUtils.isBlank(taskId)) return;
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format("开始执行预测小时泵 组织名称:%s,日期:%s,任务id:%s",
+                                orgConfig.getOrgName(), time, taskId));
+
+                //等待结果
+                ModelPredictorResult taskResult = asyncRequestUtil.wait(predictProperties.getUrl() + taskProgressUri + taskId);
+                if (taskResult == null || taskResult.getTask() == null) return;
+
+                long end = System.currentTimeMillis();
+                //任务状态
+                ModelPredictorTask task = taskResult.getTask();
+                switch (task.getStatus()) {
+                    case ModelPredictorTaskStatus.COMPLETED:
+                        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                                , String.format("执行预测小时泵成功,组织名称:%s,用时(毫秒):%d,返回:%s"
+                                        , orgConfig.getOrgName(), (end - begin), taskResult));
+                        break;
+                    case ModelPredictorTaskStatus.FAILED:
+                    case ModelPredictorTaskStatus.CANCELLED:
+                        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                                , String.format("执行预测小时泵失败,组织名称:%s,用时(毫秒):%d,返回:%s"
+                                        , orgConfig.getOrgName(), (end - begin), taskResult));
+                        break;
+                }
+            }
+        }
+    }
+
+    /**
+     * 同步小时泵
+     *
+     * @param nextDays 往后预测天数
+     */
+    public void syncHourPump(int nextDays) {
+        //计算开始和结束日期
+        LocalDate startTime = LocalDate.now();
+        LocalDate endTime = LocalDate.now().plusDays(nextDays);
+
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format("开始执行同步小时泵 开始日期:%s,结束日期:%s", startTime, endTime));
+        long begin = System.currentTimeMillis();
+
+        //执行同步
+        Boolean synced = waterVolumePredictHourService.syncStatusAndEnergy(startTime, endTime);
+
+        long end = System.currentTimeMillis();
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format("执行预测小时泵成功 开始日期:%s,结束日期:%s,用时(毫秒):%d,同步状态:%s",
+                        startTime, endTime, (end - begin), synced));
+    }
+}

+ 226 - 0
src/main/java/com/shkpr/service/aimodelpower/components/WaterPumpCollector.java

@@ -0,0 +1,226 @@
+package com.shkpr.service.aimodelpower.components;
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.aimodelpower.constants.LogFlagBusiType;
+import com.shkpr.service.aimodelpower.constants.WaterPumpStandard;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.ShizilaishuiHistoryService;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.WaterPumpCollectionConfigService;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.WaterPumpCollectionRecordService;
+import com.shkpr.service.aimodelpower.dto.ShizilaishuiHistory;
+import com.shkpr.service.aimodelpower.dto.WaterPumpCollectionConfig;
+import com.shkpr.service.aimodelpower.dto.WaterPumpCollectionRecord;
+import com.shkpr.service.aimodelpower.dto.WaterPumpEnergyConfig;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * 泵采集器
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Component
+public class WaterPumpCollector {
+    /**
+     * log
+     */
+    private final static String mStrClassName = "PumpDataCollector";
+    private final static String mBizType = LogFlagBusiType.BUSI_DATA_COLLECT.toStrValue();
+    /**
+     * 小时单位
+     */
+    private final static String HOUR_UNIT = "hour";
+    /**
+     * 小时对齐单位
+     */
+    private final static String HOUR_ALIGN_UNIT = "hh";
+
+    @Autowired
+    @Qualifier("taskExecutor")
+    ThreadPoolTaskExecutor taskScheduler;
+    @Autowired
+    WaterPumpCollectionConfigService waterPumpCollectionConfigService;
+    @Autowired
+    ShizilaishuiHistoryService shizilaishuiHistoryService;
+    @Autowired
+    WaterPumpCollectionRecordService waterPumpCollectionRecordService;
+
+    /**
+     * 采集数据
+     *
+     * @param previousHours 往前回溯小时数
+     */
+    public void collectData(int previousHours) {
+        //计算开始和结束时间,开始时间整点回溯小时
+        final LocalDateTime startTime = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS).minusHours(previousHours);
+        final LocalDateTime endTime = LocalDateTime.now();
+
+        //获取采集配置
+        Map<String, List<WaterPumpCollectionConfig>> configs = waterPumpCollectionConfigService.findAll().stream()
+                .collect(Collectors.groupingBy(WaterPumpCollectionConfig::getDeviceCode));
+
+        //遍历采集配置
+        for (Map.Entry<String, List<WaterPumpCollectionConfig>> configEntry : configs.entrySet()) {
+            taskScheduler.execute(() -> {
+                //采集标签映射
+                Map<String, WaterPumpCollectionConfig> tagMap = configEntry.getValue().stream()
+                        .collect(Collectors.toMap(
+                                WaterPumpCollectionConfig::getCollcationTag,
+                                it -> it,
+                                (a, b) -> b
+                        ));
+                //采集标签
+                List<String> tags = new ArrayList<>(tagMap.keySet());
+
+                //查询总数,排除总数小于等于0的数据
+                Long count = shizilaishuiHistoryService.findCount(startTime, endTime, tags);
+                if (CollectionUtils.isEmpty(tags) || count <= 0) return;
+
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format("开始执行采集泵数据 设备编码:%s", configEntry.getKey()));
+                long begin = System.currentTimeMillis();
+
+                //读取数据
+                List<ShizilaishuiHistory> shizilaishuiHistories = shizilaishuiHistoryService.findAlign(startTime, endTime
+                        , 1, HOUR_UNIT, HOUR_ALIGN_UNIT, tags);
+
+                //遍历原始数据,构建采集数据
+                List<WaterPumpCollectionRecord> dates = shizilaishuiHistories.stream()
+                        //根据时间分组
+                        .collect(Collectors.groupingBy(ShizilaishuiHistory::getTime))
+                        .entrySet().stream()
+                        .map(it -> {
+                            WaterPumpCollectionRecord data = new WaterPumpCollectionRecord();
+                            data.setDeviceCode(configEntry.getKey());
+                            data.setTime(it.getKey());
+                            //遍历同一台设备原始数据
+                            for (ShizilaishuiHistory shizilaishuiHistory : it.getValue()) {
+                                WaterPumpCollectionConfig config = tagMap.get(shizilaishuiHistory.getTagCode());
+                                //根据标准模式设置对应字段
+                                if (config != null) switch (config.getStandardCode()) {
+                                    case WaterPumpStandard.ACTIVE_ENERGY:
+                                        data.setActiveEnergy(shizilaishuiHistory.getVal());
+                                        break;
+                                    case WaterPumpStandard.STARTUP_STATE:
+                                        data.setStartupState(shizilaishuiHistory.getVal() != null ? shizilaishuiHistory.getVal().shortValue() : null);
+                                        break;
+                                    case WaterPumpStandard.PHASE_A_CURRENT:
+                                        data.setStartupState((short) (shizilaishuiHistory.getVal() == null || shizilaishuiHistory.getVal() == 0 ? 0 : 1));
+                                        break;
+                                }
+                            }
+                            return data;
+                        }).collect(Collectors.toList());
+
+                //执行批量合并
+                Boolean upserted = waterPumpCollectionRecordService.upsertAll(dates);
+
+                long end = System.currentTimeMillis();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format(
+                                "结束执行采集水量原始数据,设备编码:%s,入库状态:%s,用时(毫秒):%d",
+                                configEntry.getKey()
+                                , upserted
+                                , (end - begin)
+                        )
+                );
+            });
+
+        }
+    }
+
+    /**
+     * 计算电量
+     *
+     * @param previousHours 往前回溯小时数
+     */
+    public void calcPower(int previousHours) {
+        //计算开始和结束时间,开始时间整点回溯小时
+        final LocalDateTime startTime = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS).minusHours(previousHours + 1);
+        final LocalDateTime endTime = LocalDateTime.now();
+
+        //采集配置
+        List<WaterPumpCollectionConfig> collectionConfigs = waterPumpCollectionConfigService.findAll();
+        //流量配置
+        List<WaterPumpEnergyConfig> energyConfigs = waterPumpCollectionConfigService.findEnergy();
+        //设备编码
+        List<String> deviceCodes = collectionConfigs.stream()
+                .map(WaterPumpCollectionConfig::getDeviceCode)
+                .distinct().collect(Collectors.toList());
+
+        //遍历设备
+        for (String deviceCode : deviceCodes) {
+            taskScheduler.execute(() -> {
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format("开始执行计算泵电量 设备编码:%s", deviceCode));
+                long begin = System.currentTimeMillis();
+
+                //当前设备流量配置
+                WaterPumpEnergyConfig energyConfig = energyConfigs.stream()
+                        .filter(map -> Objects.equals(deviceCode, map.getPumpID()))
+                        .findFirst().orElse(null);
+
+                //读取数据
+                List<WaterPumpCollectionRecord> dates = waterPumpCollectionRecordService.findAll(
+                        startTime, endTime, deviceCode);
+                if (CollectionUtils.isEmpty(dates) || dates.size() < 2)
+                    return;
+
+                //遍历数据,跳过第一条
+                for (int i = 1; i < dates.size(); i++) {
+                    //当前记录
+                    WaterPumpCollectionRecord currentRecord = dates.get(i);
+                    //上一条记录
+                    WaterPumpCollectionRecord previousRecord = dates.get(i - 1);
+                    //获取电量
+                    Double currentEnergy = currentRecord.getActiveEnergy();
+                    Double previousEnergy = previousRecord.getActiveEnergy();
+
+                    double power;
+                    //使用计算电量
+                    if (currentEnergy != null && previousEnergy != null)
+                        power = currentEnergy - previousEnergy;
+                        //使用额度功率
+                    else if (energyConfig != null)
+                        power = energyConfig.getPower() != null ? energyConfig.getPower() : 0;
+                        //使用随机数
+                    else power = BigDecimal.valueOf(RandomUtils.nextDouble(400, 600))
+                                .setScale(2, RoundingMode.HALF_UP).doubleValue();
+
+                    //更新电量
+                    currentRecord.setPowerCons(power);
+                }
+                //移除第一条数据
+                dates.remove(0);
+
+                //执行批量合并
+                Boolean upserted = waterPumpCollectionRecordService.upsertAll(dates);
+
+                long end = System.currentTimeMillis();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format(
+                                "结束执行计算泵电量,设备编码:%s,入库状态:%s,用时(毫秒):%d",
+                                deviceCode
+                                , upserted
+                                , (end - begin)
+                        )
+                );
+            });
+        }
+    }
+}

+ 516 - 0
src/main/java/com/shkpr/service/aimodelpower/components/WaterVolumeCollector.java

@@ -0,0 +1,516 @@
+package com.shkpr.service.aimodelpower.components;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.aimodelpower.commproperties.CollectProperties;
+import com.shkpr.service.aimodelpower.constants.LogFlagBusiType;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.ShizilaishuiHistoryService;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.WaterVolumeCollectionConfigService;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.WaterVolumeCollectionRecordService;
+import com.shkpr.service.aimodelpower.dto.ShizilaishuiHistory;
+import com.shkpr.service.aimodelpower.dto.WaterVolumeCollectionConfig;
+import com.shkpr.service.aimodelpower.dto.WaterVolumeCollectionRecord;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 水量数据采集器
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Component
+public class WaterVolumeCollector {
+    /**
+     * log
+     */
+    private final static String mStrClassName = "WaterVolumeCollector";
+    private final static String mBizType = LogFlagBusiType.BUSI_DATA_COLLECT.toStrValue();
+    /**
+     * 日期格式
+     */
+    private final static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+    /**
+     * 值标识
+     */
+    private final static String VALUE_TAG = "water";
+    /**
+     * 分钟单位
+     */
+    private final static String MINUTE_UNIT = "minute";
+    /**
+     * 小时单位
+     */
+    private final static String HOUR_UNIT = "hour";
+
+    final
+    CollectProperties collectProperties;
+    final
+    ThreadPoolTaskExecutor taskScheduler;
+    final
+    ObjectMapper objectMapper;
+    final
+    WaterVolumeCollectionConfigService waterVolumeCollectionConfigService;
+    final
+    ShizilaishuiHistoryService shizilaishuiHistoryService;
+    final
+    WaterVolumeCollectionRecordService waterVolumeCollectionRecordService;
+
+
+    public WaterVolumeCollector(CollectProperties collectProperties, @Qualifier("taskExecutor") ThreadPoolTaskExecutor taskScheduler
+            , ObjectMapper objectMapper, WaterVolumeCollectionConfigService waterVolumeCollectionConfigService
+            , ShizilaishuiHistoryService shizilaishuiHistoryService, WaterVolumeCollectionRecordService waterVolumeCollectionRecordService) {
+        this.collectProperties = collectProperties;
+        this.taskScheduler = taskScheduler;
+        this.objectMapper = objectMapper;
+        this.waterVolumeCollectionConfigService = waterVolumeCollectionConfigService;
+        this.shizilaishuiHistoryService = shizilaishuiHistoryService;
+        this.waterVolumeCollectionRecordService = waterVolumeCollectionRecordService;
+    }
+
+    /**
+     * 采集营业所小时数据
+     *
+     * @param previousHours 往前回溯小时数
+     */
+    public void collectBusinessHourData(int previousHours) {
+        //自供标签配置
+        Map<String, List<String>> selfTagsMap = collectProperties.getSupplySelfTagsMap();
+        //供入标签配置
+        Map<String, List<String>> inTagsMap = collectProperties.getSupplyInTagsMap();
+        //供出标签配置
+        Map<String, List<String>> outTagsMap = collectProperties.getSupplyOutTagsMap();
+
+        //计算开始和结束时间,开始时间整点回溯小时,结束时间整点
+        final LocalDateTime startTime = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS).minusHours(previousHours);
+        final LocalDateTime endTime = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS);
+
+        //遍历自供标签
+        for (CollectProperties.CollectTagAttr collectTag : collectProperties.getSupplySelfTags()) {
+            taskScheduler.execute(() -> {
+                //采集标签
+                List<String> selfTags = selfTagsMap.get(collectTag.getName());
+                List<String> inTags = inTagsMap.get(collectTag.getName());
+                List<String> outTags = outTagsMap.get(collectTag.getName());
+
+                //查询总数,排除总数小于2的数据
+                Long count = shizilaishuiHistoryService.findCount(startTime, endTime, selfTags);
+                if (CollectionUtils.isEmpty(selfTags) || count < 2) return;
+
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format("开始执行采集水量营业所小时数据 组织名称:%s", collectTag.getName()));
+                long begin = System.currentTimeMillis();
+
+                //读取数据
+                List<ShizilaishuiHistory> shizilaishuiHistories = shizilaishuiHistoryService.findDifference(
+                        startTime, endTime, 1, HOUR_UNIT, selfTags, inTags, outTags);
+
+                //最新值
+                Double last = null;
+                //遍历原始数据,构建采集数据
+                List<WaterVolumeCollectionRecord> dates = new ArrayList<>();
+                for (ShizilaishuiHistory shizilaishuiHistory : shizilaishuiHistories) {
+                    //构建数据,时间步长到下一个整点
+                    WaterVolumeCollectionRecord data = new WaterVolumeCollectionRecord(
+                            collectTag.getName(), LocalDateTime.parse(shizilaishuiHistory.getTime(), dateTimeFormatter)
+                            .truncatedTo(ChronoUnit.HOURS).plusHours(1)
+                            .format(dateTimeFormatter), VALUE_TAG, "");
+
+                    //取值
+                    Double value = shizilaishuiHistory.getVal();
+                    //数据异常值判断,使用上一条数据
+                    if (value == null || (value > collectProperties.getMaxWater() || value < collectProperties.getMinWater())) {
+                        //上一次空值处理
+                        if (last == null)
+                            last = waterVolumeCollectionRecordService.findLastValueHour(data.getOrgName(), data.getTime(), VALUE_TAG,
+                                    collectProperties.getMaxWater(), collectProperties.getMinWater());
+                        //使用上一条数据
+                        value = last;
+                    }
+                    //正常数据缓存上一条
+                    else last = shizilaishuiHistory.getVal();
+
+                    //设置数据值
+                    data.setValue(BigDecimal.valueOf(value).toPlainString());
+                    dates.add(data);
+                }
+
+                //执行批量合并
+                Boolean upserted = waterVolumeCollectionRecordService.upsertAllHour(dates);
+
+                long end = System.currentTimeMillis();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format(
+                                "结束执行采集水量营业所小时数据,组织名称:%s,入库状态:%s,用时(毫秒):%d",
+                                collectTag.getName()
+                                , upserted
+                                , (end - begin)
+                        )
+                );
+            });
+        }
+    }
+
+    /**
+     * 采集营业所分钟数据
+     *
+     * @param previousHours 往前回溯小时数
+     */
+    public void collectBusinessMinuteData(int previousHours) {
+        //自供标签配置
+        Map<String, List<String>> selfTagsMap = collectProperties.getSupplySelfTagsMap();
+        //供入标签配置
+        Map<String, List<String>> inTagsMap = collectProperties.getSupplyInTagsMap();
+        //供出标签配置
+        Map<String, List<String>> outTagsMap = collectProperties.getSupplyOutTagsMap();
+
+        //计算开始和结束时间
+        final LocalDateTime now = LocalDateTime.now();
+        final int nowMinute = now.getMinute();
+        //开始时间整点回溯小时
+        final LocalDateTime startTime = now.truncatedTo(ChronoUnit.HOURS).minusHours(previousHours);
+        //结束时间整15分钟
+        final LocalDateTime endTime = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES).minusMinutes(nowMinute % 15);
+
+        //遍历自供标签
+        for (CollectProperties.CollectTagAttr collectTag : collectProperties.getSupplySelfTags()) {
+            taskScheduler.execute(() -> {
+                //采集标签
+                List<String> selfTags = selfTagsMap.get(collectTag.getName());
+                List<String> inTags = inTagsMap.get(collectTag.getName());
+                List<String> outTags = outTagsMap.get(collectTag.getName());
+
+                //查询总数,排除总数小于2的数据
+                Long count = shizilaishuiHistoryService.findCount(startTime, endTime, selfTags);
+                if (CollectionUtils.isEmpty(selfTags) || count < 2) return;
+
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format("开始执行采集水量营业所分钟数据 组织名称:%s", collectTag.getName()));
+                long begin = System.currentTimeMillis();
+
+                //读取数据
+                List<ShizilaishuiHistory> shizilaishuiHistories = shizilaishuiHistoryService.findDifference(
+                        startTime, endTime, 15, MINUTE_UNIT, selfTags, inTags, outTags);
+
+                //最新值
+                Double last = null;
+                //遍历原始数据,构建采集数据
+                List<WaterVolumeCollectionRecord> dates = new ArrayList<>();
+                for (ShizilaishuiHistory shizilaishuiHistory : shizilaishuiHistories) {
+                    //解析时间
+                    LocalDateTime time = LocalDateTime.parse(shizilaishuiHistory.getTime(), dateTimeFormatter);
+                    //分钟
+                    final int minute = time.getMinute();
+                    //构建数据,时间步长到下一个15分钟
+                    WaterVolumeCollectionRecord data = new WaterVolumeCollectionRecord(
+                            collectTag.getName(),
+                            time.plusMinutes(minute % 15 == 0 ? 15 : 15 - (minute % 15)).format(dateTimeFormatter),
+                            VALUE_TAG, "");
+
+                    //取值
+                    Double value = shizilaishuiHistory.getVal();
+                    //数据异常值判断,使用上一条数据
+                    if (value == null || (value > collectProperties.getMaxWater() || value < collectProperties.getMinWater())) {
+                        //上一次空值处理
+                        if (last == null)
+                            last = waterVolumeCollectionRecordService.findLastValueMinute(data.getOrgName(), data.getTime(), VALUE_TAG,
+                                    collectProperties.getMaxWater(), collectProperties.getMinWater());
+                        //使用上一条数据
+                        value = last;
+                    }
+                    //正常数据缓存上一条
+                    else last = shizilaishuiHistory.getVal();
+
+                    //设置数据和值
+                    data.setValue(BigDecimal.valueOf(value).toPlainString());
+                    dates.add(data);
+                }
+
+                //执行批量合并
+                Boolean upserted = waterVolumeCollectionRecordService.upsertAllMinute(dates);
+
+                long end = System.currentTimeMillis();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format(
+                                "结束执行采集水量营业所分钟数据,组织名称:%s,入库状态:%s,用时(毫秒):%d",
+                                collectTag.getName()
+                                , upserted
+                                , (end - begin)
+                        )
+                );
+            });
+        }
+    }
+
+    /**
+     * 采集差值小时数据
+     *
+     * @param previousHours 往前回溯小时数
+     */
+    public void collectDifferenceHourData(int previousHours) {
+        //计算开始和结束时间,开始时间整点回溯小时,结束时间整点
+        final LocalDateTime startTime = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS).minusHours(previousHours);
+        final LocalDateTime endTime = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS);
+
+        //营业所组织名称
+        List<String> selfNames = collectProperties.getSupplySelfTags().stream()
+                .map(CollectProperties.CollectTagAttr::getName)
+                .collect(Collectors.toList());
+        //获取采集配置
+        Map<String, List<WaterVolumeCollectionConfig>> configs = waterVolumeCollectionConfigService.findAll().stream()
+                //排除营业所组织
+                .filter(config -> !selfNames.contains(config.getOrgName()))
+                .collect(Collectors.groupingBy(WaterVolumeCollectionConfig::getOrgName));
+
+        //遍历采集配置
+        for (Map.Entry<String, List<WaterVolumeCollectionConfig>> config : configs.entrySet()) {
+            taskScheduler.execute(() -> {
+                //采集标签
+                List<String> tags = config.getValue().stream()
+                        .map(WaterVolumeCollectionConfig::getCollcationTag).collect(Collectors.toList());
+                //配置字段
+                String configStr = "";
+                try {
+                    configStr = objectMapper.writeValueAsString(config.getValue());
+                } catch (JsonProcessingException ignored) {
+                }
+
+                //查询总数,排除总数小于2的数据
+                Long count = shizilaishuiHistoryService.findCount(startTime, endTime, tags);
+                if (CollectionUtils.isEmpty(tags) || count < 2) return;
+
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format("开始执行采集水量差值小时数据 组织名称:%s", config.getKey()));
+                long begin = System.currentTimeMillis();
+
+                //读取数据
+                List<ShizilaishuiHistory> shizilaishuiHistories = shizilaishuiHistoryService.findDifference(
+                        startTime, endTime, 1, HOUR_UNIT, tags, null, null);
+
+                //最新值
+                Double last = null;
+                //遍历原始数据,构建采集数据
+                List<WaterVolumeCollectionRecord> dates = new ArrayList<>();
+                for (ShizilaishuiHistory shizilaishuiHistory : shizilaishuiHistories) {
+                    //构建数据,时间取截止时间
+                    WaterVolumeCollectionRecord data = new WaterVolumeCollectionRecord(
+                            config.getKey(), LocalDateTime.parse(shizilaishuiHistory.getTime(), dateTimeFormatter)
+                            .plusHours(1).format(dateTimeFormatter), VALUE_TAG, configStr);
+
+                    //取值
+                    Double value = shizilaishuiHistory.getVal();
+                    //数据异常值判断,使用上一条数据
+                    if (value == null || (value > collectProperties.getMaxWater() || value < collectProperties.getMinWater())) {
+                        //上一次空值处理
+                        if (last == null)
+                            last = waterVolumeCollectionRecordService.findLastValueHour(data.getOrgName(), data.getTime(), VALUE_TAG,
+                                    collectProperties.getMaxWater(), collectProperties.getMinWater());
+                        //使用上一条数据
+                        value = last;
+                    }
+                    //正常数据缓存上一条
+                    else last = shizilaishuiHistory.getVal();
+
+                    //设置数据值
+                    data.setValue(BigDecimal.valueOf(value).toPlainString());
+                    dates.add(data);
+                }
+
+                //执行批量合并
+                Boolean upserted = waterVolumeCollectionRecordService.upsertAllHour(dates);
+
+                long end = System.currentTimeMillis();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format(
+                                "结束执行采集水量差值小时数据,组织名称:%s,入库状态:%s,用时(毫秒):%d",
+                                config.getKey()
+                                , upserted
+                                , (end - begin)
+                        )
+                );
+            });
+        }
+    }
+
+    /**
+     * 采集差值分钟数据
+     *
+     * @param previousHours 往前回溯小时数
+     */
+    public void collectDifferenceMinuteData(int previousHours) {
+        //计算开始和结束时间
+        final LocalDateTime now = LocalDateTime.now();
+        final int nowMinute = now.getMinute();
+        //开始时间整点回溯小时
+        final LocalDateTime startTime = now.truncatedTo(ChronoUnit.HOURS).minusHours(previousHours);
+        //结束时间整15分钟
+        final LocalDateTime endTime = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES).minusMinutes(nowMinute % 15);
+
+        //营业所组织名称
+        List<String> selfNames = collectProperties.getSupplySelfTags().stream()
+                .map(CollectProperties.CollectTagAttr::getName)
+                .collect(Collectors.toList());
+        //获取采集配置
+        Map<String, List<WaterVolumeCollectionConfig>> configs = waterVolumeCollectionConfigService.findAll().stream()
+                //排除营业所组织
+                .filter(config -> !selfNames.contains(config.getOrgName()))
+                .collect(Collectors.groupingBy(WaterVolumeCollectionConfig::getOrgName));
+
+        //遍历采集配置
+        for (Map.Entry<String, List<WaterVolumeCollectionConfig>> config : configs.entrySet()) {
+            taskScheduler.execute(() -> {
+                //采集标签
+                List<String> tags = config.getValue().stream()
+                        .map(WaterVolumeCollectionConfig::getCollcationTag).collect(Collectors.toList());
+                //配置字段
+                String configStr = "";
+                try {
+                    configStr = objectMapper.writeValueAsString(config.getValue());
+                } catch (JsonProcessingException ignored) {
+                }
+
+                //查询总数,排除总数小于2的数据
+                Long count = shizilaishuiHistoryService.findCount(startTime, endTime, tags);
+                if (CollectionUtils.isEmpty(tags) || count < 2) return;
+
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format("开始执行采集水量差值分钟数据 组织名称:%s", config.getKey()));
+                long begin = System.currentTimeMillis();
+
+                //读取数据
+                List<ShizilaishuiHistory> shizilaishuiHistories = shizilaishuiHistoryService.findDifference(
+                        startTime, endTime, 15, MINUTE_UNIT, tags, null, null);
+
+                //最新值
+                Double last = null;
+                //遍历原始数据,构建采集数据
+                List<WaterVolumeCollectionRecord> dates = new ArrayList<>();
+                for (ShizilaishuiHistory shizilaishuiHistory : shizilaishuiHistories) {
+                    //解析时间
+                    LocalDateTime time = LocalDateTime.parse(shizilaishuiHistory.getTime(), dateTimeFormatter);
+                    //分钟
+                    final int minute = time.getMinute();
+                    //构建数据,时间步长到下一个15分钟
+                    WaterVolumeCollectionRecord data = new WaterVolumeCollectionRecord(
+                            config.getKey(),
+                            time.plusMinutes(minute % 15 == 0 ? 15 : 15 - (minute % 15)).format(dateTimeFormatter),
+                            VALUE_TAG, configStr);
+
+                    //取值
+                    Double value = shizilaishuiHistory.getVal();
+                    //数据异常值判断,使用上一条数据
+                    if (value == null || (value > collectProperties.getMaxWater() || value < collectProperties.getMinWater())) {
+                        //上一次空值处理
+                        if (last == null)
+                            last = waterVolumeCollectionRecordService.findLastValueMinute(data.getOrgName(), data.getTime(), VALUE_TAG,
+                                    collectProperties.getMaxWater(), collectProperties.getMinWater());
+                        //使用上一条数据
+                        value = last;
+                    }
+                    //正常数据缓存上一条
+                    else last = shizilaishuiHistory.getVal();
+
+                    //设置数据值
+                    data.setValue(BigDecimal.valueOf(value).toPlainString());
+                    dates.add(data);
+                }
+
+                //执行批量合并
+                Boolean upserted = waterVolumeCollectionRecordService.upsertAllMinute(dates);
+
+                long end = System.currentTimeMillis();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format(
+                                "结束执行采集水量差值分钟数据,组织名称:%s,入库状态:%s,用时(毫秒):%d",
+                                config.getKey()
+                                , upserted
+                                , (end - begin)
+                        )
+                );
+            });
+        }
+    }
+
+    /**
+     * 采集原始数据
+     *
+     * @param previousHours 往前回溯小时数
+     */
+    public void collectRawData(int previousHours) {
+        //计算开始和结束时间,开始时间整日回溯小时
+        final LocalDateTime startTime = LocalDateTime.now().truncatedTo(ChronoUnit.DAYS).minusHours(previousHours);
+        final LocalDateTime endTime = LocalDateTime.now();
+
+        //营业所组织名称
+        List<String> selfNames = collectProperties.getSupplySelfTags().stream()
+                .map(CollectProperties.CollectTagAttr::getName)
+                .collect(Collectors.toList());
+        //获取采集配置
+        Map<String, List<WaterVolumeCollectionConfig>> configs = waterVolumeCollectionConfigService.findAll().stream()
+                //排除营业所组织
+                .filter(config -> !selfNames.contains(config.getOrgName()))
+                .collect(Collectors.groupingBy(WaterVolumeCollectionConfig::getOrgName));
+
+        //遍历采集配置
+        for (Map.Entry<String, List<WaterVolumeCollectionConfig>> config : configs.entrySet()) {
+            taskScheduler.execute(() -> {
+                //采集标签
+                List<String> tags = config.getValue().stream()
+                        .map(WaterVolumeCollectionConfig::getCollcationTag).collect(Collectors.toList());
+
+                //查询总数,排除总数小于等于0的数据
+                Long count = shizilaishuiHistoryService.findCount(startTime, endTime, tags);
+                if (CollectionUtils.isEmpty(tags) || count <= 0) return;
+
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format("开始执行采集水量原始数据 组织名称:%s", config.getKey()));
+                long begin = System.currentTimeMillis();
+
+                //读取数据
+                List<ShizilaishuiHistory> shizilaishuiHistories = shizilaishuiHistoryService.findAl(startTime, endTime, tags);
+
+                //遍历原始数据,构建采集数据
+                List<WaterVolumeCollectionRecord> dates = shizilaishuiHistories.stream().map(shizilaishuiHistory -> {
+                    WaterVolumeCollectionRecord data = new WaterVolumeCollectionRecord();
+                    data.setTime(String.valueOf(LocalDateTime.parse(shizilaishuiHistory.getTime(), dateTimeFormatter)
+                            .atZone(ZoneId.systemDefault())
+                            .toInstant().toEpochMilli()));
+                    data.setUpdateTime(String.valueOf(LocalDateTime.parse(shizilaishuiHistory.getUpdateTime(), dateTimeFormatter)
+                            .atZone(ZoneId.systemDefault())
+                            .toInstant().toEpochMilli()));
+                    data.setOrgName(shizilaishuiHistory.getName());
+                    data.setCollectionTag(shizilaishuiHistory.getTagCode());
+                    data.setValue(BigDecimal.valueOf(shizilaishuiHistory.getVal()).toPlainString());
+                    return data;
+                }).collect(Collectors.toList());
+
+                //执行批量合并
+                Boolean upserted = waterVolumeCollectionRecordService.upsertAllRaw(dates);
+
+                long end = System.currentTimeMillis();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format(
+                                "结束执行采集水量原始数据,组织名称:%s,入库状态:%s,用时(毫秒):%d",
+                                config.getKey()
+                                , upserted
+                                , (end - begin)
+                        )
+                );
+            });
+        }
+    }
+}

+ 5 - 5
src/main/java/com/shkpr/service/aimodelpower/configuration/ChildPgDataSourceConfig.java

@@ -1,6 +1,5 @@
 package com.shkpr.service.aimodelpower.configuration;
 
-import com.shkpr.service.aimodelpower.globalcache.GlobalData;
 import com.zaxxer.hikari.HikariConfig;
 import com.zaxxer.hikari.HikariDataSource;
 import org.apache.ibatis.session.SqlSessionFactory;
@@ -9,15 +8,12 @@ import org.mybatis.spring.SqlSessionTemplate;
 import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.boot.jdbc.DataSourceBuilder;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.PropertySource;
 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
 import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 
-import javax.annotation.PostConstruct;
 import javax.sql.DataSource;
 
 /**
@@ -28,7 +24,10 @@ import javax.sql.DataSource;
  * @Version V1.0
  **/
 @Configuration
-@MapperScan(basePackages = "com.shkpr.service.aimodelpower.dbdao.mapper.childpg",sqlSessionFactoryRef = "db2SqlSessionFactory")
+@MapperScan(basePackages = "com.shkpr.service.aimodelpower.dbdao.mapperwatervolume"
+        ,sqlSessionFactoryRef = "childSqlSessionFactory"
+,sqlSessionTemplateRef = "childSqlSessionTemplate"
+)
 @PropertySource(value = "file:${global.sql.config.path}", ignoreResourceNotFound = true, encoding="utf-8")
 public class ChildPgDataSourceConfig {
 
@@ -102,6 +101,7 @@ public class ChildPgDataSourceConfig {
     public SqlSessionFactory childSqlSessionFactoryBean(@Qualifier("childDatasource") DataSource dataSource) throws Exception {
         SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
         sessionFactoryBean.setDataSource(dataSource);
+        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
 
         org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
         configuration.setCallSettersOnNulls(true);       //数据库中字段值为null时也要求返回

+ 89 - 0
src/main/java/com/shkpr/service/aimodelpower/configuration/OracleDataSourceConfig.java

@@ -0,0 +1,89 @@
+package com.shkpr.service.aimodelpower.configuration;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.HikariDataSource;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.mybatis.spring.SqlSessionTemplate;
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
+
+import javax.sql.DataSource;
+@Configuration
+@MapperScan(basePackages = "com.shkpr.service.aimodelpower.dbdao.mapperoracle",
+        sqlSessionFactoryRef = "oracleSqlSessionFactory",
+        sqlSessionTemplateRef = "oracleSqlSessionTemplate")
+@PropertySource(value = "file:${global.sql.config.path}", ignoreResourceNotFound = true, encoding="utf-8")
+public class OracleDataSourceConfig {
+
+    @Value("${spring.datasource.oracle.driver-class-name:}")
+    private String driveClass = "oracle.jdbc.driver.OracleDriver";
+
+    @Value("${spring.datasource.oracle.jdbc-url:}")
+    private String url = "";
+
+    @Value("${spring.datasource.oracle.username:}")
+    private String username = "";
+
+    @Value("${spring.datasource.oracle.password:}")
+    private String password = "";
+
+    @Bean(name = "oracleDatasource")
+    public DataSource dataSource() {
+        HikariDataSource obj = new HikariDataSource(getConfig());
+        try {
+            obj.setLoginTimeout((int)(30000/1000));
+        }catch (Exception e){}
+        return obj;
+        //return DataSourceBuilder.create().build();
+        //Spring Boot 2.x默认使用HikariCP
+    }
+
+    private HikariConfig getConfig() {
+        HikariConfig hikariConfig = new HikariConfig();
+        hikariConfig.setDriverClassName(driveClass);
+        hikariConfig.setJdbcUrl(url);
+        hikariConfig.setUsername(username);
+        hikariConfig.setPassword(password);
+        hikariConfig.setMaximumPoolSize(200);
+        hikariConfig.setMinimumIdle(1);
+        hikariConfig.setConnectionTestQuery("SELECT 1 FROM DUAL");
+        hikariConfig.setMaxLifetime(120000);
+        hikariConfig.setIdleTimeout(30000);
+        hikariConfig.setConnectionTimeout(30000);
+        hikariConfig.setValidationTimeout(30000);
+        hikariConfig.setInitializationFailTimeout(-1);
+
+        return hikariConfig;
+    }
+
+    @Bean("oracleSqlSessionFactory")
+    public SqlSessionFactory oracleSqlSessionFactoryBean(@Qualifier("oracleDatasource") DataSource dataSource) throws Exception {
+        SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
+        sessionFactoryBean.setDataSource(dataSource);
+        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
+
+        org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
+        configuration.setCallSettersOnNulls(true);       //数据库中字段值为null时也要求返回
+        configuration.setMapUnderscoreToCamelCase(true); //开启驼峰映射
+        configuration.setCacheEnabled(false);
+        sessionFactoryBean.setConfiguration(configuration);
+        return sessionFactoryBean.getObject();
+    }
+
+    @Bean("oracleSqlSessionTemplate")
+    public SqlSessionTemplate oracleSqlSessionTemplate(@Qualifier("oracleSqlSessionFactory") SqlSessionFactory sessionFactory) {
+        return new SqlSessionTemplate(sessionFactory);
+    }
+
+    @Bean(name = "oracleDbTransactionManager")
+    public DataSourceTransactionManager oracleDbTransactionManager(@Qualifier("oracleDatasource") DataSource dataSource){
+        return new DataSourceTransactionManager(dataSource);
+    }
+}

+ 57 - 15
src/main/java/com/shkpr/service/aimodelpower/configuration/ScheduleTaskConfiguration.java

@@ -5,17 +5,20 @@ import com.global.base.log.LogPrintMgr;
 import com.shkpr.service.aimodelpower.commtools.TimeTool;
 import com.shkpr.service.aimodelpower.constants.LogFlagBusiType;
 import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.boot.task.TaskExecutorBuilder;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.scheduling.TaskScheduler;
 import org.springframework.scheduling.annotation.AsyncConfigurer;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.SchedulingConfigurer;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
 import org.springframework.scheduling.config.ScheduledTaskRegistrar;
 
 import javax.annotation.PostConstruct;
 import java.lang.reflect.Method;
+import java.time.Duration;
 import java.util.Timer;
 import java.util.TimerTask;
 import java.util.concurrent.Executor;
@@ -23,55 +26,94 @@ import java.util.concurrent.Executor;
 @Configuration
 @EnableScheduling
 public class ScheduleTaskConfiguration implements SchedulingConfigurer, AsyncConfigurer {
-    private String mStrClassName;
+    private final String mStrClassName;
+
     public ScheduleTaskConfiguration() {
         mStrClassName = this.getClass().getSimpleName();
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public Executor getAsyncExecutor() {
-        Executor executor = taskScheduler();
-        return executor;
+        return asyncTaskExecutor();
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
         return new AsyncUncaughtExceptionHandler() {
             @Override
             public void handleUncaughtException(Throwable ex, Method method, Object... params) {
-                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, LogFlagBusiType.BUSI_INIT.toStrValue(), mStrClassName
-                        ,String.format("AsyncUncaughtException(method="+method.getName()+", msg="+ex.getMessage()+")..."));
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, LogFlagBusiType.BUSI_INIT.toStrValue(), mStrClassName, String.format("AsyncUncaughtException(method=" + method.getName() + ", msg=" + ex.getMessage() + ")..."));
             }
         };
     }
 
+    /**
+     * {@inheritDoc }
+     */
     @Override
     public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
         TaskScheduler taskScheduler = taskScheduler();
         scheduledTaskRegistrar.setTaskScheduler(taskScheduler);
     }
 
-    /** 定时任务多线程处理 */
-    @Bean(destroyMethod = "shutdown")
-    public ThreadPoolTaskScheduler taskScheduler(){
+    /**
+     * 定时任务多线程执行器
+     */
+    @Bean(destroyMethod = "shutdown", name = "timeThreadPoolTaskScheduler")
+    public ThreadPoolTaskScheduler taskScheduler() {
         ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
-        scheduler.setPoolSize(5);
-        scheduler.setThreadNamePrefix("Time-Task-");
-        //设置线程池中任务的等待时间,如果超过这个时间还没有销毁就强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
-        //如:kill [PID]后,最大等待时长为5秒钟
+        scheduler.setPoolSize(8);
+        scheduler.setThreadNamePrefix("TimeTask-");
         scheduler.setAwaitTerminationSeconds(5);
-        //设置线程池关闭的时候等待所有任务都完成后,再继续销毁其他的Bean,这样这些异步任务的销毁就会先于数据库连接池对象的销毁
         scheduler.setWaitForTasksToCompleteOnShutdown(true);
         return scheduler;
     }
 
+    /**
+     * 任务多线程执行器
+     */
+    @Bean(destroyMethod = "shutdown", name = "taskExecutor")
+    public ThreadPoolTaskExecutor taskExecutor() {
+        TaskExecutorBuilder builder = new TaskExecutorBuilder();
+        builder = builder.queueCapacity(Integer.MAX_VALUE);
+        builder = builder.corePoolSize(8);
+        builder = builder.maxPoolSize(Integer.MAX_VALUE);
+        builder = builder.allowCoreThreadTimeOut(true);   // 允许核心线程超时回收:减少空闲资源占用[1](@ref)
+        builder = builder.keepAlive(Duration.ofSeconds(30)); // 非核心线程空闲存活时间:建议30-60秒[1,4](@ref)
+        builder = builder.threadNamePrefix("Task-");  // 线程名前缀:便于监控和日志追踪[1,4](@ref)
+        return builder.build();
+    }
+
+    /**
+     * 异步任务多线程执行器
+     *
+     * @return 线程任务执行
+     */
+    @Bean(destroyMethod = "shutdown", name = "asyncThreadPoolTaskExecutor")
+    public ThreadPoolTaskExecutor asyncTaskExecutor() {
+        TaskExecutorBuilder builder = new TaskExecutorBuilder();
+        builder = builder.queueCapacity(Integer.MAX_VALUE);
+        builder = builder.corePoolSize(2);
+        builder = builder.maxPoolSize(Integer.MAX_VALUE);
+        builder = builder.allowCoreThreadTimeOut(true);   // 允许核心线程超时回收:减少空闲资源占用[1](@ref)
+        builder = builder.keepAlive(Duration.ofSeconds(30)); // 非核心线程空闲存活时间:建议30-60秒[1,4](@ref)
+        builder = builder.threadNamePrefix("AsyncTask-");  // 线程名前缀:便于监控和日志追踪[1,4](@ref)
+        return builder.build();
+    }
+
     @PostConstruct
-    public void init(){
+    public void init() {
         new Timer().schedule(new TimerTask() {
             @Override
             public void run() {
                 TimeTool.refreshUTCTimeRes();
             }
-        }, 1000);//延时1秒执行
+        }, 10000);
     }
 }

+ 1 - 1
src/main/java/com/shkpr/service/aimodelpower/configuration/SecondSourceConfiguration.java

@@ -17,7 +17,7 @@ import javax.annotation.PostConstruct;
 import javax.sql.DataSource;
 
 @Configuration
-@MapperScan(basePackages = "com.shkpr.service.aimodelpower.dbdao.mapper2th", sqlSessionTemplateRef = "secondSqlSessionTemplate")
+@MapperScan(basePackages = "com.shkpr.service.aimodelpower.dbdao.mapperoracle", sqlSessionTemplateRef = "secondSqlSessionTemplate")
 @PropertySource(value = "file:${global.sql.config.path}", ignoreResourceNotFound = true, encoding="utf-8")
 public class SecondSourceConfiguration {
     @Value("${spring.datasource.data2.driver-class-name:}")

+ 3 - 0
src/main/java/com/shkpr/service/aimodelpower/constants/LogFlagBusiType.java

@@ -65,6 +65,9 @@ public enum  LogFlagBusiType {
     BUSI_WARN_TYPE_MGR(61, "Warn Type Mgr Biz"),
     BUSI_CALL_TASK_AS(62, "Call Task As Biz"),
 
+    BUSI_DATA_COLLECT(63,"Data Collect"),
+    BUSI_MODEL_PREDICT(63,"Model Predict"),
+
     BUSI_INTERNAL_WARN_EVENT_MAIN_TYPE(96,"Internal Warn Event Main Type Biz"),
     BUSI_INTERNAL_WARN_SUG_DEFINE(97,"Internal Warn Sug Define Biz"),
     BUSI_INTERNAL_WARN_EVENT_SUG_LINK(98,"Internal Warn Event Sug Link Biz"),

+ 30 - 0
src/main/java/com/shkpr/service/aimodelpower/constants/ModelPredictorTaskStatus.java

@@ -0,0 +1,30 @@
+package com.shkpr.service.aimodelpower.constants;
+
+/**
+ * 预测任务状态
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+public interface ModelPredictorTaskStatus {
+    /**
+     * 等待中
+     */
+    String PENDING = "pending";
+    /**
+     * 运行中
+     */
+    String RUNNING = "running";
+    /**
+     * 已完成
+     */
+    String COMPLETED = "completed";
+    /**
+     * 失败
+     */
+    String FAILED = "failed";
+    /**
+     * 已取消
+     */
+    String CANCELLED = "cancelled";
+}

+ 22 - 0
src/main/java/com/shkpr/service/aimodelpower/constants/WaterPumpStandard.java

@@ -0,0 +1,22 @@
+package com.shkpr.service.aimodelpower.constants;
+
+/**
+ * 泵标准
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+public interface WaterPumpStandard {
+    /**
+     * 电量
+     */
+    String ACTIVE_ENERGY = "active_energy";
+    /**
+     * 启停状态
+     */
+    String STARTUP_STATE = "startup_state";
+    /**
+     *
+     */
+    String PHASE_A_CURRENT = "phase_a_current";
+}

+ 0 - 67
src/main/java/com/shkpr/service/aimodelpower/dbdao/mapper2th/PatrolObjectAttMapper.java

@@ -1,67 +0,0 @@
-package com.shkpr.service.aimodelpower.dbdao.mapper2th;
-
-import com.shkpr.service.aimodelpower.dbdao.providers.PatrolObjectAttSqlProvider;
-import org.apache.ibatis.annotations.*;
-
-import java.util.List;
-import java.util.Map;
-
-public interface PatrolObjectAttMapper {
-    @SelectProvider(type = PatrolObjectAttSqlProvider.class, method = "totalCounts")
-    int totalCounts(@Param("andWheres") Map<String, Object> andWheres, @Param("orWheres") Map<String, Object> orWheres, @Param("extend") String extend);
-
-    @SelectProvider(type = PatrolObjectAttSqlProvider.class, method = "listAllWiths")
-    List<Map<String, Object>> listAllWiths(@Param("table") String table, @Param("filed") String filed
-            , @Param("limit") int limit
-            , @Param("offset") int offset
-            , @Param("andWheres") Map<String, Object> andWheres
-            , @Param("orWheres") Map<String, Object> orWheres
-            , @Param("order") String order
-            , @Param("extend") String extend);
-
-    @SelectProvider(type = PatrolObjectAttSqlProvider.class, method = "getByUniqueId")
-    Map<String, Object> getOne(@Param("table") String table, @Param("filed") String filed, @Param("id") Object id);
-
-    @SelectProvider(type = PatrolObjectAttSqlProvider.class, method = "batchQueryIn")
-    List<Map<String, Object>> batchQueryIn(@Param("table") String table, @Param("filed") String filed
-            , @Param("ids") List<? extends Object> ids
-            , @Param("order") String order
-            , @Param("extend") String extend);
-
-    @SelectProvider(type = PatrolObjectAttSqlProvider.class, method = "batchQueryWiths")
-    List<Map<String, Object>> batchQueryWiths(@Param("table") String table, @Param("filed") String filed
-            , @Param("andWheres") Map<String, Object> andWheres
-            , @Param("orWheres") Map<String, Object> orWheres
-            , @Param("order") String order
-            , @Param("extend") String extend);
-
-    @InsertProvider(type = PatrolObjectAttSqlProvider.class, method = "inserts")
-    int inserts(@Param("inserts") Map<String, Object> inserts);
-
-    @InsertProvider(type = PatrolObjectAttSqlProvider.class, method = "batchInserts")
-    int batchInserts(@Param("inserts") List<Map<String, Object>> inserts);
-
-    /**
-     * update和delete返回受影响条数需要配合useAffectedRows=true
-     * @param datas
-     * @param andWheres
-     * @return
-     */
-    @UpdateProvider(type = PatrolObjectAttSqlProvider.class, method = "updateWiths")
-    int updateWiths(@Param("datas") Map<String, Object> datas
-            , @Param("datasEx") Map<String, Object> datasEx
-            , @Param("andWheres") Map<String, Object> andWheres
-            , @Param("orWheres") Map<String, Object> orWheres
-            , @Param("extend") String extend);
-
-    @SelectProvider(type = PatrolObjectAttSqlProvider.class, method = "existsLine")
-    boolean existLine(@Param("table") String table
-            , @Param("andWheres") Map<String, Object> andWheres
-            , @Param("orWheres") Map<String, Object> orWheres
-            , @Param("extend") String extend);
-
-    @DeleteProvider(type = PatrolObjectAttSqlProvider.class, method = "deleteWiths")
-    int deleteWithsEx(@Param("andWheres") Map<String, Object> andWheres
-            , @Param("orWheres") Map<String, Object> orWheres
-            , @Param("extend") String extend);
-}

+ 85 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperoracle/ShizilaishuiHistoryMapper.java

@@ -0,0 +1,85 @@
+package com.shkpr.service.aimodelpower.dbdao.mapperoracle;
+
+import com.shkpr.service.aimodelpower.dto.ShizilaishuiHistory;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.cursor.Cursor;
+
+import java.util.List;
+
+/**
+ * 市自来水历史mapper
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Mapper
+public interface ShizilaishuiHistoryMapper {
+    /**
+     * 根据采集标签查询差值
+     *
+     * @param beginTime    开始时间
+     * @param endTime      结束时间
+     * @param interval     采集间隔
+     * @param intervalUnit 采集间隔单位
+     * @param selfTags     自供标签
+     * @param inTags       供入标签
+     * @param outTags      供出标签
+     * @return 历史数据
+     */
+    Cursor<ShizilaishuiHistory> findDifferenceByTimeAndTags(
+            @Param("beginTime") String beginTime,
+            @Param("endTime") String endTime,
+            @Param("interval") String interval,
+            @Param("intervalUnit") String intervalUnit,
+            @Param("selfTags") List<String> selfTags,
+            @Param("inTags") List<String> inTags,
+            @Param("outTags") List<String> outTags);
+
+    /**
+     * 根据采集标签查询对齐值
+     *
+     * @param beginTime    开始时间
+     * @param endTime      结束时间
+     * @param interval     采集间隔
+     * @param intervalUnit 采集间隔单位
+     * @param alignUnit    对齐单位
+     * @param selfTags     自供标签
+     * @return 历史数据
+     */
+    Cursor<ShizilaishuiHistory> findAlignByTimeAndTags(
+            @Param("beginTime") String beginTime,
+            @Param("endTime") String endTime,
+            @Param("interval") String interval,
+            @Param("intervalUnit") String intervalUnit,
+            @Param("alignUnit") String alignUnit,
+            @Param("selfTags") List<String> selfTags
+    );
+
+    /**
+     * 根据采集标签查询
+     *
+     * @param beginTime 开始时间
+     * @param endTime   结束时间
+     * @param selfTags  自供标签
+     * @return 历史数据
+     */
+    Cursor<ShizilaishuiHistory> findAllByTimeAndTags(
+            @Param("beginTime") String beginTime,
+            @Param("endTime") String endTime,
+            @Param("selfTags") List<String> selfTags);
+
+
+    /**
+     * 根据采集标签查询条数
+     *
+     * @param beginTime 开始时间
+     * @param endTime   结束时间
+     * @param selfTags  自供标签
+     * @return 条数
+     */
+    Long findCountByTimeAndTags(@Param("beginTime") String beginTime,
+                                @Param("endTime") String endTime,
+                                @Param("selfTags") List<String> selfTags);
+
+}

+ 22 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperwatervolume/OrgConfigMapper.java

@@ -0,0 +1,22 @@
+package com.shkpr.service.aimodelpower.dbdao.mapperwatervolume;
+
+import com.shkpr.service.aimodelpower.dto.OrgConfig;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 组织配置mapper
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Mapper
+public interface OrgConfigMapper {
+    /**
+     * 查询全部
+     *
+     * @return 实体集合
+     */
+    List<OrgConfig> findAll();
+}

+ 30 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperwatervolume/WaterPumpCollectionConfigMapper.java

@@ -0,0 +1,30 @@
+package com.shkpr.service.aimodelpower.dbdao.mapperwatervolume;
+
+import com.shkpr.service.aimodelpower.dto.WaterPumpCollectionConfig;
+import com.shkpr.service.aimodelpower.dto.WaterPumpEnergyConfig;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 水量采集配置mapper
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Mapper
+public interface WaterPumpCollectionConfigMapper {
+    /**
+     * 查询全部
+     *
+     * @return 实体集合
+     */
+    List<WaterPumpCollectionConfig> findAll();
+
+    /**
+     * 查询额定流量
+     *
+     * @return 实体集合
+     */
+     List<WaterPumpEnergyConfig> findEnergy();
+}

+ 34 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperwatervolume/WaterPumpCollectionRecordMapper.java

@@ -0,0 +1,34 @@
+package com.shkpr.service.aimodelpower.dbdao.mapperwatervolume;
+
+import com.shkpr.service.aimodelpower.dto.WaterPumpCollectionRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.cursor.Cursor;
+
+/**
+ * 水量采集数据mapper
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Mapper
+public interface WaterPumpCollectionRecordMapper {
+    /**
+     * 合并操作原始数据
+     *
+     * @param record 对象
+     * @return 合并数量
+     */
+    int upsert(WaterPumpCollectionRecord record);
+
+    /**
+     * 根据时间和设备编码查询全部
+     *
+     * @param beginTime  开始时间
+     * @param endTime    结束时间
+     * @param deviceCode 设备编码
+     * @return 实体集合
+     */
+    Cursor<WaterPumpCollectionRecord> findAllByTimeAndDeviceCode(@Param("beginTime") String beginTime
+            , @Param("endTime") String endTime, @Param("deviceCode") String deviceCode);
+}

+ 22 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperwatervolume/WaterVolumeCollectionConfigMapper.java

@@ -0,0 +1,22 @@
+package com.shkpr.service.aimodelpower.dbdao.mapperwatervolume;
+
+import com.shkpr.service.aimodelpower.dto.WaterVolumeCollectionConfig;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+/**
+ * 水量采集配置mapper
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Mapper
+public interface WaterVolumeCollectionConfigMapper {
+    /**
+     * 查询全部
+     *
+     * @return 实体集合
+     */
+    List<WaterVolumeCollectionConfig> findAll();
+}

+ 66 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperwatervolume/WaterVolumeCollectionRecordMapper.java

@@ -0,0 +1,66 @@
+package com.shkpr.service.aimodelpower.dbdao.mapperwatervolume;
+
+import com.shkpr.service.aimodelpower.dto.WaterVolumeCollectionRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * 水量采集数据mapper
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Mapper
+public interface WaterVolumeCollectionRecordMapper {
+    /**
+     * 合并操作原始数据
+     *
+     * @param record 对象
+     * @return 合并数量
+     */
+    int upsertRaw(WaterVolumeCollectionRecord record);
+
+    /**
+     * 合并操作小时数据
+     *
+     * @param record 对象
+     * @return 合并数量
+     */
+    int upsertHour(WaterVolumeCollectionRecord record);
+
+    /**
+     * 合并操作分钟数据
+     *
+     * @param record 对象
+     * @return 合并数量
+     */
+    int upsertMinute(WaterVolumeCollectionRecord record);
+
+    /**
+     * 查询上一条小时值
+     *
+     * @param orgName  组织名称
+     * @param time     时间
+     * @param valueTag 值标签
+     * @param maxValue 最大值
+     * @param minValue 最小值
+     * @return 小时值
+     */
+    Double findLastValueHour(@Param("orgName") String orgName
+            , @Param("time") String time, @Param("valueTag") String valueTag
+            , @Param("maxValue") Double maxValue, @Param("minValue") Double minValue);
+
+    /**
+     * 查询上一条分钟值
+     *
+     * @param orgName  组织名称
+     * @param time     时间
+     * @param valueTag 值标签
+     * @param maxValue 最大值
+     * @param minValue 最小值
+     * @return 小时值
+     */
+    Double findLastValueMinute(@Param("orgName") String orgName
+            , @Param("time") String time, @Param("valueTag") String valueTag
+            , @Param("maxValue") Double maxValue, @Param("minValue") Double minValue);
+}

+ 43 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/mapperwatervolume/WaterVolumePredictHourMapper.java

@@ -0,0 +1,43 @@
+package com.shkpr.service.aimodelpower.dbdao.mapperwatervolume;
+
+import com.shkpr.service.aimodelpower.dto.WaterVolumePredictHour;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.cursor.Cursor;
+
+/**
+ * 水量预测小时mapper
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Mapper
+public interface WaterVolumePredictHourMapper {
+    /**
+     * 合并操作分钟数据
+     *
+     * @param record 对象
+     * @return 合并数量
+     */
+    int upsertMinute(WaterVolumePredictHour record);
+
+    /**
+     * 根据时间同步状态和电量
+     *
+     * @param beginDate 开始时间
+     * @param endDate   结束时间
+     * @return 同步数量
+     */
+    int syncStatusAndEnergyByTime(@Param("beginDate") String beginDate, @Param("endDate") String endDate);
+
+    /**
+     * 根据时间和组织id查询小时数据
+     *
+     * @param beginDate 开始时间
+     * @param endDate   结束时间
+     * @param orgId     组织id
+     * @return 小时数据集合
+     */
+    Cursor<WaterVolumePredictHour> findHourByTimeAndOrgId(@Param("beginDate") String beginDate
+            , @Param("endDate") String endDate, @Param("orgId") String orgId);
+}

+ 34 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/OrgConfigServiceImpl.java

@@ -0,0 +1,34 @@
+package com.shkpr.service.aimodelpower.dbdao.services;
+
+import com.shkpr.service.aimodelpower.dbdao.mapperwatervolume.OrgConfigMapper;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.OrgConfigService;
+import com.shkpr.service.aimodelpower.dto.OrgConfig;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 组织配置service实现
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Service
+public class OrgConfigServiceImpl implements OrgConfigService {
+    final
+    OrgConfigMapper orgConfigMapper;
+
+    public OrgConfigServiceImpl(OrgConfigMapper orgConfigMapper) {
+        this.orgConfigMapper = orgConfigMapper;
+    }
+
+    /**
+     * 查询全部
+     *
+     * @return 实体集合
+     */
+    @Override
+    public List<OrgConfig> findAll() {
+        return orgConfigMapper.findAll();
+    }
+}

+ 207 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/ShizilaishuiHistoryServiceImpl.java

@@ -0,0 +1,207 @@
+package com.shkpr.service.aimodelpower.dbdao.services;
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.aimodelpower.constants.LogFlagBusiType;
+import com.shkpr.service.aimodelpower.dbdao.mapperoracle.ShizilaishuiHistoryMapper;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.ShizilaishuiHistoryService;
+import com.shkpr.service.aimodelpower.dto.ShizilaishuiHistory;
+import org.apache.ibatis.cursor.Cursor;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 市自来水历史service实现
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Service
+public class ShizilaishuiHistoryServiceImpl implements ShizilaishuiHistoryService {
+    /**
+     * log
+     */
+    private final static String mStrClassName = "ShizilaishuiHistoryServiceImpl";
+    private final static String mBizType = LogFlagBusiType.BUSI_DATA_COLLECT.toStrValue();
+
+    /**
+     * 日期格式
+     */
+    private final static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    final
+    SqlSessionFactory oracleSqlSessionFactory;
+    final
+    ShizilaishuiHistoryMapper shizilaishuiHistoryMapper;
+
+    public ShizilaishuiHistoryServiceImpl(@Qualifier("oracleSqlSessionFactory") SqlSessionFactory oracleSqlSessionFactory, ShizilaishuiHistoryMapper shizilaishuiHistoryMapper) {
+        this.oracleSqlSessionFactory = oracleSqlSessionFactory;
+        this.shizilaishuiHistoryMapper = shizilaishuiHistoryMapper;
+    }
+
+    /**
+     * 根据采集标签查询
+     *
+     * @param beginTime    开始时间
+     * @param endTime      结束时间
+     * @param interval     采集间隔
+     * @param intervalUnit 采集间隔单位
+     * @param selfTags     自供标签
+     * @param inTags       供入标签
+     * @param outTags      供出标签
+     * @return 历史数据
+     */
+    @Override
+    @Transactional(transactionManager = "oracleDbTransactionManager")
+    public List<ShizilaishuiHistory> findDifference(LocalDateTime beginTime, LocalDateTime endTime
+            , Integer interval, String intervalUnit, List<String> selfTags, List<String> inTags, List<String> outTags) {
+        //获取session和mapper
+        try (SqlSession session = oracleSqlSessionFactory.openSession()) {
+            ShizilaishuiHistoryMapper mapper = session.getMapper(ShizilaishuiHistoryMapper.class);
+
+            //市自来水历史集合
+            List<ShizilaishuiHistory> shizilaishuiHistories = new ArrayList<>();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName,
+                    String.format("开始根据时间和标签查询自来水差值数据, 开启事务和游标 开始时间:%s, 结束时间:%s, 间隔:%d, 间隔单位:%s, 采集标签:%s",
+                            beginTime, endTime, interval, intervalUnit, selfTags));
+            long begin = System.currentTimeMillis();
+
+            //获取游标
+            try (Cursor<ShizilaishuiHistory> cursor = mapper.findDifferenceByTimeAndTags(
+                    beginTime.format(dateTimeFormatter), endTime.format(dateTimeFormatter),
+                    interval.toString(), intervalUnit, selfTags, inTags, outTags)) {
+                //迭代游标
+                for (ShizilaishuiHistory shizilaishuiHistory : cursor) {
+                    //检查游标完成
+                    if (cursor.isConsumed()) break;
+                    //填入数据
+                    shizilaishuiHistories.add(shizilaishuiHistory);
+                }
+            } catch (IOException e) {
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName, String.format("关闭游标失败  error:%s", e));
+            }
+
+            long end = System.currentTimeMillis();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName,
+                    String.format("完成根据时间和标签查询自来水差值数据, 关闭事务和游标 开始时间:%s, 结束时间:%s, 间隔:%d, 间隔单位:%s, 用时(毫秒):%d",
+                            beginTime, endTime, interval, intervalUnit, (end - begin)));
+
+            return shizilaishuiHistories;
+        }
+    }
+
+    /**
+     * 查询对齐值
+     *
+     * @param beginTime    开始时间
+     * @param endTime      结束时间
+     * @param interval     采集间隔
+     * @param intervalUnit 采集间隔单位
+     * @param alignUnit    对齐单位
+     * @param selfTags     自供标签
+     * @return 历史数据
+     */
+    @Override
+    @Transactional(transactionManager = "oracleDbTransactionManager")
+    public List<ShizilaishuiHistory> findAlign(LocalDateTime beginTime, LocalDateTime endTime
+            , Integer interval, String intervalUnit, String alignUnit, List<String> selfTags) {
+        //获取session和mapper
+        try (SqlSession session = oracleSqlSessionFactory.openSession()) {
+            ShizilaishuiHistoryMapper mapper = session.getMapper(ShizilaishuiHistoryMapper.class);
+
+            //市自来水历史集合
+            List<ShizilaishuiHistory> shizilaishuiHistories = new ArrayList<>();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName,
+                    String.format("开始根据时间和标签查询自来水对齐数据, 开启事务和游标 开始时间:%s, 结束时间:%s, 间隔:%d, 间隔单位:%s ,对齐单位:%s, 采集标签:%s",
+                            beginTime, endTime, interval, intervalUnit, alignUnit, selfTags));
+            long begin = System.currentTimeMillis();
+
+            //获取游标
+            try (Cursor<ShizilaishuiHistory> cursor = mapper.findAlignByTimeAndTags(beginTime.format(dateTimeFormatter),
+                    endTime.format(dateTimeFormatter), interval.toString(), intervalUnit, alignUnit, selfTags)) {
+                //迭代游标
+                for (ShizilaishuiHistory shizilaishuiHistory : cursor) {
+                    //检查游标完成
+                    if (cursor.isConsumed()) break;
+                    //填入数据
+                    shizilaishuiHistories.add(shizilaishuiHistory);
+                }
+            } catch (IOException e) {
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName, String.format("关闭游标失败  error:%s", e));
+            }
+
+            long end = System.currentTimeMillis();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName,
+                    String.format("完成根据时间和标签查询自来水对齐数据, 关闭事务和游标 开始时间:%s, 结束时间:%s, 间隔:%d, 间隔单位:%s, 用时(毫秒):%d",
+                            beginTime, endTime, interval, intervalUnit, (end - begin)));
+
+            return shizilaishuiHistories;
+        }
+    }
+
+    /**
+     * 查询数据
+     *
+     * @param beginTime 开始时间
+     * @param endTime   结束时间
+     * @param selfTags  自供标签
+     * @return 历史数据
+     */
+    @Override
+    @Transactional(transactionManager = "oracleDbTransactionManager")
+    public List<ShizilaishuiHistory> findAl(LocalDateTime beginTime, LocalDateTime endTime, List<String> selfTags) {
+        //获取session和mapper
+        try (SqlSession session = oracleSqlSessionFactory.openSession()) {
+            ShizilaishuiHistoryMapper mapper = session.getMapper(ShizilaishuiHistoryMapper.class);
+
+            //市自来水历史集合
+            List<ShizilaishuiHistory> shizilaishuiHistories = new ArrayList<>();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName,
+                    String.format("开始根据时间和标签查询自来水历史数据, 开启事务和游标 开始时间:%s, 结束时间:%s, 采集标签:%s", beginTime, endTime, selfTags));
+            long begin = System.currentTimeMillis();
+
+            //获取游标
+            try (Cursor<ShizilaishuiHistory> cursor = mapper.findAllByTimeAndTags(
+                    beginTime.format(dateTimeFormatter), endTime.format(dateTimeFormatter), selfTags)) {
+                //迭代游标
+                for (ShizilaishuiHistory shizilaishuiHistory : cursor) {
+                    //检查游标完成
+                    if (cursor.isConsumed()) break;
+                    //填入数据
+                    shizilaishuiHistories.add(shizilaishuiHistory);
+                }
+            } catch (IOException e) {
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName, String.format("关闭游标失败  error:%s", e));
+            }
+
+            long end = System.currentTimeMillis();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName,
+                    String.format("完成根据时间和标签查询自来水历史数据, 关闭事务和游标 开始时间:%s, 结束时间:%s, 用时(毫秒):%d",
+                            beginTime, endTime, (end - begin)));
+
+            return shizilaishuiHistories;
+        }
+    }
+
+    /**
+     * 根据采集标签查询条数
+     *
+     * @param beginTime 开始时间
+     * @param endTime   结束时间
+     * @param selfTags  自供标签
+     * @return 历史数据
+     */
+    @Override
+    public Long findCount(LocalDateTime beginTime, LocalDateTime endTime, List<String> selfTags) {
+        return shizilaishuiHistoryMapper.findCountByTimeAndTags(beginTime.format(dateTimeFormatter), endTime.format(dateTimeFormatter), selfTags);
+    }
+}

+ 46 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/WaterPumpCollectionConfigServiceImpl.java

@@ -0,0 +1,46 @@
+package com.shkpr.service.aimodelpower.dbdao.services;
+
+import com.shkpr.service.aimodelpower.dbdao.mapperwatervolume.WaterPumpCollectionConfigMapper;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.WaterPumpCollectionConfigService;
+import com.shkpr.service.aimodelpower.dto.WaterPumpCollectionConfig;
+import com.shkpr.service.aimodelpower.dto.WaterPumpEnergyConfig;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 泵采集配置service实现
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Service
+public class WaterPumpCollectionConfigServiceImpl implements WaterPumpCollectionConfigService {
+    final
+    WaterPumpCollectionConfigMapper waterPumpCollectionConfigMapper;
+
+    public WaterPumpCollectionConfigServiceImpl(WaterPumpCollectionConfigMapper waterPumpCollectionConfigMapper) {
+        this.waterPumpCollectionConfigMapper = waterPumpCollectionConfigMapper;
+    }
+
+    /**
+     * 查询全部
+     *
+     * @return 实体集合
+     */
+    @Override
+    public List<WaterPumpCollectionConfig> findAll() {
+        return waterPumpCollectionConfigMapper.findAll();
+    }
+
+    /**
+     * 查询额定流量
+     *
+     * @return 实体集合
+     */
+    @Override
+    public List<WaterPumpEnergyConfig> findEnergy() {
+        return waterPumpCollectionConfigMapper.findEnergy();
+    }
+
+}

+ 160 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/WaterPumpCollectionRecordServiceImpl.java

@@ -0,0 +1,160 @@
+package com.shkpr.service.aimodelpower.dbdao.services;
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.aimodelpower.constants.LogFlagBusiType;
+import com.shkpr.service.aimodelpower.dbdao.mapperwatervolume.WaterPumpCollectionRecordMapper;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.WaterPumpCollectionRecordService;
+import com.shkpr.service.aimodelpower.dto.WaterPumpCollectionRecord;
+import org.apache.ibatis.cursor.Cursor;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+/**
+ * 水量采集数据service实现
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Service
+public class WaterPumpCollectionRecordServiceImpl implements WaterPumpCollectionRecordService {
+    /**
+     * log
+     */
+    private final static String mStrClassName = "WaterVolumeCollectionRecordServiceImpl";
+    private final static String mBizType = LogFlagBusiType.BUSI_DATA_COLLECT.toStrValue();
+    /**
+     * 日期格式
+     */
+    private final static DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    final
+    SqlSessionFactory childSqlSessionFactory;
+    final
+    WaterPumpCollectionRecordMapper waterPumpCollectionRecordMapper;
+
+    public WaterPumpCollectionRecordServiceImpl(@Qualifier("childSqlSessionFactory") SqlSessionFactory childSqlSessionFactory, WaterPumpCollectionRecordMapper waterPumpCollectionRecordMapper) {
+        this.childSqlSessionFactory = childSqlSessionFactory;
+        this.waterPumpCollectionRecordMapper = waterPumpCollectionRecordMapper;
+    }
+
+    /**
+     * 批量合并操作原始数据
+     *
+     * @param records 对象集合
+     * @return 合并状态
+     */
+    @Override
+    public Boolean upsertAll(List<WaterPumpCollectionRecord> records) {
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "开始批量写入泵数据,开启批处理 数据量:%s"
+                        , records.size()
+                )
+        );
+        long begin = System.currentTimeMillis();
+
+        //开启批处理
+        try (SqlSession sqlSession = childSqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
+            try {
+                //设置手动提交
+                Connection conn = sqlSession.getConnection();
+                conn.setAutoCommit(false);
+
+                //从session获取mapper
+                WaterPumpCollectionRecordMapper mapper = sqlSession.getMapper(WaterPumpCollectionRecordMapper.class);
+
+                //批量合并
+                records.forEach(mapper::upsert);
+
+                //发送sql至数据库
+                sqlSession.flushStatements();
+                //提交
+                sqlSession.commit();
+                conn.commit();
+
+                long end = System.currentTimeMillis();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format(
+                                "结束批量写入泵数据,提交并关闭批处理 用时(毫秒):%d"
+                                , (end - begin)
+                        )
+                );
+                return true;
+            } catch (Exception e) {
+                //回滚
+                sqlSession.rollback();
+
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                        , String.format(
+                                "批量写入泵数据失败,回滚操作 error:%s"
+                                , e
+                        )
+                );
+
+                return false;
+            }
+        }
+    }
+
+    /**
+     * 查询全部
+     *
+     * @param beginTime  开始时间
+     * @param endTime    结束时间
+     * @param deviceCode 设备编码
+     * @return 实体集合
+     */
+    @Override
+    public List<WaterPumpCollectionRecord> findAll(LocalDateTime beginTime, LocalDateTime endTime, String deviceCode) {
+        //获取session和mapper
+        try (SqlSession session = childSqlSessionFactory.openSession()) {
+            WaterPumpCollectionRecordMapper mapper = session.getMapper(WaterPumpCollectionRecordMapper.class);
+
+            //泵原始数据集合
+            List<WaterPumpCollectionRecord> waterPumpCollectionRecords = new ArrayList<>();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName,
+                    String.format("开始根据时间和设备标识查询泵原始数据, 开启事务和游标 开始时间:%s, 结束时间:%s, 设备标识:%s",
+                            beginTime, endTime, deviceCode));
+            long begin = System.currentTimeMillis();
+
+            //获取游标
+            try (Cursor<WaterPumpCollectionRecord> cursor = mapper.findAllByTimeAndDeviceCode(beginTime.format(dateTimeFormatter),
+                    endTime.format(dateTimeFormatter), deviceCode)) {
+                //迭代游标
+                for (WaterPumpCollectionRecord waterPumpCollectionRecord : cursor) {
+                    //检查游标完成
+                    if (cursor.isConsumed()) break;
+                    //填入数据
+                    waterPumpCollectionRecords.add(waterPumpCollectionRecord);
+                }
+            } catch (IOException e) {
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName, String.format("关闭游标失败  error:%s", e));
+            }
+
+            long end = System.currentTimeMillis();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName,
+                    String.format("完成根据时间和设备标识查询泵原始数据, 关闭事务和游标 开始时间:%s, 结束时间:%s, 设备标识:%s, 用时(毫秒):%d",
+                            beginTime, endTime, deviceCode, (end - begin)));
+
+            return waterPumpCollectionRecords.stream()
+                    //根据时间正序
+                    .sorted(Comparator.comparing(record ->
+                            LocalDateTime.parse(record.getTime(), dateTimeFormatter)))
+                    .collect(Collectors.toList());
+        }
+    }
+}

+ 34 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/WaterVolumeCollectionConfigServiceImpl.java

@@ -0,0 +1,34 @@
+package com.shkpr.service.aimodelpower.dbdao.services;
+
+import com.shkpr.service.aimodelpower.dbdao.mapperwatervolume.WaterVolumeCollectionConfigMapper;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.WaterVolumeCollectionConfigService;
+import com.shkpr.service.aimodelpower.dto.WaterVolumeCollectionConfig;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 水量采集配置service实现
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Service
+public class WaterVolumeCollectionConfigServiceImpl implements WaterVolumeCollectionConfigService {
+    final
+    WaterVolumeCollectionConfigMapper waterVolumeCollectionConfigMapper;
+
+    public WaterVolumeCollectionConfigServiceImpl(WaterVolumeCollectionConfigMapper waterVolumeCollectionConfigMapper) {
+        this.waterVolumeCollectionConfigMapper = waterVolumeCollectionConfigMapper;
+    }
+
+    /**
+     * 查询全部
+     *
+     * @return 实体集合
+     */
+    @Override
+    public List<WaterVolumeCollectionConfig> findAll() {
+        return waterVolumeCollectionConfigMapper.findAll();
+    }
+}

+ 249 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/WaterVolumeCollectionRecordServiceImpl.java

@@ -0,0 +1,249 @@
+package com.shkpr.service.aimodelpower.dbdao.services;
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.aimodelpower.constants.LogFlagBusiType;
+import com.shkpr.service.aimodelpower.dbdao.mapperwatervolume.WaterVolumeCollectionRecordMapper;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.WaterVolumeCollectionRecordService;
+import com.shkpr.service.aimodelpower.dto.WaterVolumeCollectionRecord;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+import java.sql.Connection;
+import java.util.List;
+
+
+/**
+ * 水量采集数据service实现
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Service
+public class WaterVolumeCollectionRecordServiceImpl implements WaterVolumeCollectionRecordService {
+    /**
+     * log
+     */
+    private final static String mStrClassName = "WaterVolumeCollectionRecordServiceImpl";
+    private final static String mBizType = LogFlagBusiType.BUSI_DATA_COLLECT.toStrValue();
+
+    final
+    SqlSessionFactory childSqlSessionFactory;
+    final
+    WaterVolumeCollectionRecordMapper waterVolumeCollectionRecordMapper;
+
+    public WaterVolumeCollectionRecordServiceImpl(@Qualifier("childSqlSessionFactory") SqlSessionFactory childSqlSessionFactory, WaterVolumeCollectionRecordMapper waterVolumeCollectionRecordMapper) {
+        this.childSqlSessionFactory = childSqlSessionFactory;
+        this.waterVolumeCollectionRecordMapper = waterVolumeCollectionRecordMapper;
+    }
+
+    /**
+     * 批量合并操作原始数据
+     *
+     * @param records 对象集合
+     * @return 合并状态
+     */
+    @Override
+    public Boolean upsertAllRaw(List<WaterVolumeCollectionRecord> records) {
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "开始批量写入水量原始数据,开启批处理 数据量:%s"
+                        , records.size()
+                )
+        );
+        long begin = System.currentTimeMillis();
+
+        //开启批处理
+        try (SqlSession sqlSession = childSqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
+            try {
+                //设置手动提交
+                Connection conn = sqlSession.getConnection();
+                conn.setAutoCommit(false);
+
+                //从session获取mapper
+                WaterVolumeCollectionRecordMapper mapper = sqlSession.getMapper(WaterVolumeCollectionRecordMapper.class);
+
+                //批量合并
+                records.forEach(mapper::upsertRaw);
+
+                //发送sql至数据库
+                sqlSession.flushStatements();
+                //提交
+                sqlSession.commit();
+                conn.commit();
+
+                long end = System.currentTimeMillis();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format(
+                                "结束批量写入水量原始数据,提交并关闭批处理 用时(毫秒):%d"
+                                , (end - begin)
+                        )
+                );
+                return true;
+            } catch (Exception e) {
+                //回滚
+                sqlSession.rollback();
+
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                        , String.format(
+                                "批量写入水量原始数据失败,回滚操作 error:%s"
+                                , e
+                        )
+                );
+
+                return false;
+            }
+        }
+    }
+
+    /**
+     * 批量合并操作小时数据
+     *
+     * @param records 对象集合
+     * @return 合并状态
+     */
+    @Override
+    public Boolean upsertAllHour(List<WaterVolumeCollectionRecord> records) {
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "开始批量写入水量小时数据,开启批处理 数据量:%s"
+                        , records.size()
+                )
+        );
+        long begin = System.currentTimeMillis();
+
+        //开启批处理
+        try (SqlSession sqlSession = childSqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
+            try {
+                //设置手动提交
+                Connection conn = sqlSession.getConnection();
+                conn.setAutoCommit(false);
+
+                //从session获取mapper
+                WaterVolumeCollectionRecordMapper mapper = sqlSession.getMapper(WaterVolumeCollectionRecordMapper.class);
+
+                //批量合并
+                records.forEach(mapper::upsertHour);
+
+                //发送sql至数据库
+                sqlSession.flushStatements();
+                //提交
+                sqlSession.commit();
+                conn.commit();
+
+                long end = System.currentTimeMillis();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format(
+                                "结束批量写入水量小时数据,提交并关闭批处理 用时(毫秒):%d"
+                                , (end - begin)
+                        )
+                );
+                return true;
+            } catch (Exception e) {
+                //回滚
+                sqlSession.rollback();
+
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                        , String.format(
+                                "批量写入水量小时数据失败,回滚操作 error:%s"
+                                , e
+                        )
+                );
+
+                return false;
+            }
+        }
+    }
+
+    /**
+     * 批量合并操作分钟数据
+     *
+     * @param records 对象集合
+     * @return 合并状态
+     */
+    @Override
+    public Boolean upsertAllMinute(List<WaterVolumeCollectionRecord> records) {
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "开始批量写入水量分钟数据,开启批处理 数据量:%s"
+                        , records.size()
+                )
+        );
+        long begin = System.currentTimeMillis();
+
+        //开启批处理
+        try (SqlSession sqlSession = childSqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
+            try {
+                //设置手动提交
+                Connection conn = sqlSession.getConnection();
+                conn.setAutoCommit(false);
+
+                //从session获取mapper
+                WaterVolumeCollectionRecordMapper dataPressCurMapper = sqlSession.getMapper(WaterVolumeCollectionRecordMapper.class);
+
+                //批量合并
+                records.forEach(dataPressCurMapper::upsertMinute);
+
+                //发送sql至数据库
+                sqlSession.flushStatements();
+                //提交
+                sqlSession.commit();
+                conn.commit();
+
+                long end = System.currentTimeMillis();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format(
+                                "结束批量写入水量分钟数据,提交并关闭批处理 用时(毫秒):%d"
+                                , (end - begin)
+                        )
+                );
+                return true;
+            } catch (Exception e) {
+                //回滚
+                sqlSession.rollback();
+
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                        , String.format(
+                                "批量写入水量分钟数据失败,回滚操作 error:%s"
+                                , e
+                        )
+                );
+
+                return false;
+            }
+        }
+    }
+
+    /**
+     * 查询上一条小时值
+     *
+     * @param orgName  组织名称
+     * @param time     时间
+     * @param valueTag 值标签
+     * @param maxValue 最大值
+     * @param minValue 最小值
+     * @return 小时值
+     */
+    @Override
+    public Double findLastValueHour(String orgName, String time, String valueTag, Double maxValue, Double minValue) {
+        return waterVolumeCollectionRecordMapper.findLastValueHour(orgName, time, valueTag, maxValue, minValue);
+    }
+
+    /**
+     * 查询上一条分钟值
+     *
+     * @param orgName  组织名称
+     * @param time     时间
+     * @param valueTag 值标签
+     * @param maxValue 最大值
+     * @param minValue 最小值
+     * @return 分钟值
+     */
+    @Override
+    public Double findLastValueMinute(String orgName, String time, String valueTag, Double maxValue, Double minValue) {
+        return waterVolumeCollectionRecordMapper.findLastValueMinute(orgName, time, valueTag, maxValue, minValue);
+    }
+}

+ 166 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/WaterVolumePredictHourServiceImpl.java

@@ -0,0 +1,166 @@
+package com.shkpr.service.aimodelpower.dbdao.services;
+
+import com.global.base.log.LogLevelFlag;
+import com.global.base.log.LogPrintMgr;
+import com.shkpr.service.aimodelpower.constants.LogFlagBusiType;
+import com.shkpr.service.aimodelpower.dbdao.mapperwatervolume.WaterVolumePredictHourMapper;
+import com.shkpr.service.aimodelpower.dbdao.services.intef.WaterVolumePredictHourService;
+import com.shkpr.service.aimodelpower.dto.WaterVolumePredictHour;
+import org.apache.ibatis.cursor.Cursor;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.sql.Connection;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 水量预测小时service实现
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Service
+public class WaterVolumePredictHourServiceImpl implements WaterVolumePredictHourService {
+    /**
+     * log
+     */
+    private final static String mStrClassName = "WaterVolumeCollectionRecordServiceImpl";
+    private final static String mBizType = LogFlagBusiType.BUSI_MODEL_PREDICT.toStrValue();
+
+    final
+    SqlSessionFactory childSqlSessionFactory;
+    final
+    WaterVolumePredictHourMapper waterVolumePredictHourMapper;
+
+    public WaterVolumePredictHourServiceImpl(@Qualifier("childSqlSessionFactory") SqlSessionFactory childSqlSessionFactory, WaterVolumePredictHourMapper waterVolumePredictHourMapper) {
+        this.childSqlSessionFactory = childSqlSessionFactory;
+        this.waterVolumePredictHourMapper = waterVolumePredictHourMapper;
+    }
+
+    /**
+     * 批量合并操作分钟数据
+     *
+     * @param records 对象集合
+     * @return 合并状态
+     */
+    @Override
+    public Boolean upsertAllMinute(List<WaterVolumePredictHour> records) {
+        LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                , String.format(
+                        "开始批量写入水量预测分钟数据,开启批处理 数据量:%s"
+                        , records.size()
+                )
+        );
+        long begin = System.currentTimeMillis();
+
+        //开启批处理
+        try (SqlSession sqlSession = childSqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
+            try {
+                //设置手动提交
+                Connection conn = sqlSession.getConnection();
+                conn.setAutoCommit(false);
+
+                //从session获取mapper
+                WaterVolumePredictHourMapper mapper = sqlSession.getMapper(WaterVolumePredictHourMapper.class);
+
+                //批量合并
+                records.forEach(mapper::upsertMinute);
+
+                //发送sql至数据库
+                sqlSession.flushStatements();
+                //提交
+                sqlSession.commit();
+                conn.commit();
+
+                long end = System.currentTimeMillis();
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName
+                        , String.format(
+                                "结束批量写入水量预测分钟数据,提交并关闭批处理 用时(毫秒):%d"
+                                , (end - begin)
+                        )
+                );
+                return true;
+            } catch (Exception e) {
+                //回滚
+                sqlSession.rollback();
+
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
+                        , String.format(
+                                "批量写入水量预测分钟数据失败,回滚操作 error:%s"
+                                , e
+                        )
+                );
+
+                return false;
+            }
+        }
+    }
+
+    /**
+     * 根据时间同步状态和电量
+     *
+     * @param beginDate 开始时间
+     * @param endDate   结束时间
+     * @return 同步数量
+     */
+    @Override
+    public Boolean syncStatusAndEnergy(LocalDate beginDate, LocalDate endDate) {
+        return waterVolumePredictHourMapper.syncStatusAndEnergyByTime(beginDate.toString(), endDate.toString()) > 0;
+    }
+
+    /**
+     * 查询小时数据
+     *
+     * @param beginDate 开始时间
+     * @param endDate   结束时间
+     * @param orgId     组织id
+     * @return 小时数据集合
+     */
+    @Override
+    public List<WaterVolumePredictHour> findHour(LocalDate beginDate, LocalDate endDate, String orgId) {
+        //获取session和mapper
+        try (SqlSession session = childSqlSessionFactory.openSession()) {
+            WaterVolumePredictHourMapper mapper = session.getMapper(WaterVolumePredictHourMapper.class);
+
+            //数据集合
+            List<WaterVolumePredictHour> dates = new ArrayList<>();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName,
+                    String.format("开始根据时间和组织id查询水量预测小时数据, 开启事务和游标 开始时间:%s, 结束时间:%s, 组织id:%s",
+                            beginDate, endDate, orgId));
+            long begin = System.currentTimeMillis();
+
+            //获取游标
+            try (Cursor<WaterVolumePredictHour> cursor = mapper.findHourByTimeAndOrgId(beginDate.toString(),
+                    endDate.toString(), orgId)) {
+                //迭代游标
+                for (WaterVolumePredictHour data : cursor) {
+                    //检查游标完成
+                    if (cursor.isConsumed()) break;
+                    //填入数据
+                    dates.add(data);
+                }
+            } catch (IOException e) {
+                LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName, String.format("关闭游标失败  error:%s", e));
+            }
+
+            long end = System.currentTimeMillis();
+            LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_INFO, mBizType, mStrClassName,
+                    String.format("完成根据时间和组织id查询水量预测小时数据, 关闭事务和游标 开始时间:%s, 结束时间:%s, 组织id:%s, 用时(毫秒):%d",
+                            beginDate, endDate, orgId, (end - begin)));
+
+            return dates.stream()
+                    //根据时间正序
+                    .sorted(Comparator.comparing(WaterVolumePredictHour::getDate)
+                            .thenComparing(WaterVolumePredictHour::getHour))
+                    .collect(Collectors.toList());
+        }
+    }
+}

+ 20 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/OrgConfigService.java

@@ -0,0 +1,20 @@
+package com.shkpr.service.aimodelpower.dbdao.services.intef;
+
+import com.shkpr.service.aimodelpower.dto.OrgConfig;
+
+import java.util.List;
+
+/**
+ * 组织配置service实现
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+public interface OrgConfigService {
+    /**
+     * 查询全部
+     *
+     * @return 实体集合
+     */
+    List<OrgConfig> findAll();
+}

+ 64 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/ShizilaishuiHistoryService.java

@@ -0,0 +1,64 @@
+package com.shkpr.service.aimodelpower.dbdao.services.intef;
+
+import com.shkpr.service.aimodelpower.dto.ShizilaishuiHistory;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 市自来水历史service
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+public interface ShizilaishuiHistoryService {
+    /**
+     * 查询差值
+     *
+     * @param beginTime    开始时间
+     * @param endTime      结束时间
+     * @param interval     采集间隔
+     * @param intervalUnit 采集间隔单位
+     * @param selfTags     自供标签
+     * @param inTags       供入标签
+     * @param outTags      供出标签
+     * @return 历史数据
+     */
+    List<ShizilaishuiHistory> findDifference(LocalDateTime beginTime, LocalDateTime endTime, Integer interval
+            , String intervalUnit, List<String> selfTags, List<String> inTags, List<String> outTags);
+
+    /**
+     * 查询对齐值
+     *
+     * @param beginTime    开始时间
+     * @param endTime      结束时间
+     * @param interval     采集间隔
+     * @param intervalUnit 采集间隔单位
+     * @param alignUnit    对齐单位
+     * @param selfTags     自供标签
+     * @return 历史数据
+     */
+    List<ShizilaishuiHistory> findAlign(LocalDateTime beginTime, LocalDateTime endTime, Integer interval
+            , String intervalUnit, String alignUnit, List<String> selfTags
+    );
+
+    /**
+     * 查询数据
+     *
+     * @param beginTime 开始时间
+     * @param endTime   结束时间
+     * @param selfTags  自供标签
+     * @return 历史数据
+     */
+    List<ShizilaishuiHistory> findAl(LocalDateTime beginTime, LocalDateTime endTime, List<String> selfTags);
+
+    /**
+     * 查询条数
+     *
+     * @param beginTime 开始时间
+     * @param endTime   结束时间
+     * @param selfTags  自供标签
+     * @return 条数
+     */
+    Long findCount(LocalDateTime beginTime, LocalDateTime endTime, List<String> selfTags);
+}

+ 27 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/WaterPumpCollectionConfigService.java

@@ -0,0 +1,27 @@
+package com.shkpr.service.aimodelpower.dbdao.services.intef;
+
+import com.shkpr.service.aimodelpower.dto.WaterPumpCollectionConfig;
+import com.shkpr.service.aimodelpower.dto.WaterPumpEnergyConfig;
+
+import java.util.List;
+
+/**
+ * 泵采集配置service实现
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+public interface WaterPumpCollectionConfigService {
+    /**
+     * 查询全部
+     *
+     * @return 实体集合
+     */
+    List<WaterPumpCollectionConfig> findAll();
+
+    /**
+     * 查询额定流量
+     * @return 实体集合
+     */
+    List<WaterPumpEnergyConfig> findEnergy();
+}

+ 32 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/WaterPumpCollectionRecordService.java

@@ -0,0 +1,32 @@
+package com.shkpr.service.aimodelpower.dbdao.services.intef;
+
+import com.shkpr.service.aimodelpower.dto.WaterPumpCollectionRecord;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 泵采集数据service
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+public interface WaterPumpCollectionRecordService {
+    /**
+     * 批量合并操作原始数据
+     *
+     * @param records 对象集合
+     * @return 合并状态
+     */
+    Boolean upsertAll(List<WaterPumpCollectionRecord> records);
+
+    /**
+     * 查询全部
+     *
+     * @param beginTime  开始时间
+     * @param endTime    结束时间
+     * @param deviceCode 设备编码
+     * @return 实体集合
+     */
+    List<WaterPumpCollectionRecord> findAll(LocalDateTime beginTime, LocalDateTime endTime, String deviceCode);
+}

+ 20 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/WaterVolumeCollectionConfigService.java

@@ -0,0 +1,20 @@
+package com.shkpr.service.aimodelpower.dbdao.services.intef;
+
+import com.shkpr.service.aimodelpower.dto.WaterVolumeCollectionConfig;
+
+import java.util.List;
+
+/**
+ * 水量采集配置service
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+public interface WaterVolumeCollectionConfigService {
+    /**
+     * 查询全部
+     *
+     * @return 实体集合
+     */
+    List<WaterVolumeCollectionConfig> findAll();
+}

+ 61 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/WaterVolumeCollectionRecordService.java

@@ -0,0 +1,61 @@
+package com.shkpr.service.aimodelpower.dbdao.services.intef;
+
+import com.shkpr.service.aimodelpower.dto.WaterVolumeCollectionRecord;
+
+import java.util.List;
+
+/**
+ * 水量采集数据service
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+public interface WaterVolumeCollectionRecordService {
+    /**
+     * 批量合并操作原始数据
+     *
+     * @param records 对象集合
+     * @return 合并状态
+     */
+    Boolean upsertAllRaw(List<WaterVolumeCollectionRecord> records);
+
+    /**
+     * 批量合并操作小时数据
+     *
+     * @param records 对象集合
+     * @return 合并状态
+     */
+    Boolean upsertAllHour(List<WaterVolumeCollectionRecord> records);
+
+    /**
+     * 批量合并操作分钟数据
+     *
+     * @param records 对象集合
+     * @return 合并状态
+     */
+    Boolean upsertAllMinute(List<WaterVolumeCollectionRecord> records);
+
+    /**
+     * 查询上一条小时值
+     *
+     * @param orgName  组织名称
+     * @param time     时间
+     * @param valueTag 值标签
+     * @param maxValue 最大值
+     * @param minValue 最小值
+     * @return 小时值
+     */
+    Double findLastValueHour(String orgName, String time, String valueTag, Double maxValue, Double minValue);
+
+    /**
+     * 查询上一条分钟值
+     *
+     * @param orgName  组织名称
+     * @param time     时间
+     * @param valueTag 值标签
+     * @param maxValue 最大值
+     * @param minValue 最小值
+     * @return 分钟值
+     */
+    Double findLastValueMinute(String orgName, String time, String valueTag, Double maxValue, Double minValue);
+}

+ 41 - 0
src/main/java/com/shkpr/service/aimodelpower/dbdao/services/intef/WaterVolumePredictHourService.java

@@ -0,0 +1,41 @@
+package com.shkpr.service.aimodelpower.dbdao.services.intef;
+
+import com.shkpr.service.aimodelpower.dto.WaterVolumePredictHour;
+
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * 水量预测小时service实现
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+public interface WaterVolumePredictHourService {
+    /**
+     * 批量合并操作分钟数据
+     *
+     * @param records 对象集合
+     * @return 合并状态
+     */
+    Boolean upsertAllMinute(List<WaterVolumePredictHour> records);
+
+    /**
+     * 根据时间同步状态和电量
+     *
+     * @param beginDate 开始时间
+     * @param endDate   结束时间
+     * @return 同步数量
+     */
+    Boolean syncStatusAndEnergy(LocalDate beginDate, LocalDate endDate);
+
+    /**
+     * 查询小时数据
+     *
+     * @param beginDate 开始时间
+     * @param endDate   结束时间
+     * @param orgId     组织id
+     * @return 小时数据集合
+     */
+    List<WaterVolumePredictHour> findHour(LocalDate beginDate, LocalDate endDate, String orgId);
+}

+ 35 - 0
src/main/java/com/shkpr/service/aimodelpower/dto/ModelPredictorResult.java

@@ -0,0 +1,35 @@
+package com.shkpr.service.aimodelpower.dto;
+
+import com.fasterxml.jackson.annotation.JsonAlias;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+
+/**
+ * 模型预测结果
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+public class ModelPredictorResult {
+    /**
+     * true|false
+     */
+    @JsonAlias("Result")
+    String result;
+    /**
+     * 响应数据
+     */
+    @JsonAlias("KeyValue")
+    Object keyValue;
+    /**
+     * 错误信息(如果有)
+     */
+    @JsonAlias("ErrorMessage")
+    String errorMessage;
+    /**
+     * 任务
+     */
+    @JsonIgnore
+    ModelPredictorTask task;
+}

+ 45 - 0
src/main/java/com/shkpr/service/aimodelpower/dto/ModelPredictorTask.java

@@ -0,0 +1,45 @@
+package com.shkpr.service.aimodelpower.dto;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.Map;
+
+/**
+ * 预测任务
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+public class ModelPredictorTask {
+    /**
+     * 任务id
+     */
+    private String taskId;
+    /**
+     * 任务类型
+     */
+    private String taskType;
+    /**
+     * 任务状态
+     * <p>请参阅:{@link com.shkpr.service.aimodelpower.constants.ModelPredictorTaskStatus}</p>
+     */
+    private String status;
+    /**
+     * 组织id
+     */
+    private String orgId;
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createdTime;
+    /**
+     * 开始时间
+     */
+    private LocalDateTime startTime;
+    /**
+     * 进度详情
+     */
+    private Map<String, Object> progress;
+}

+ 25 - 0
src/main/java/com/shkpr/service/aimodelpower/dto/OrgConfig.java

@@ -0,0 +1,25 @@
+package com.shkpr.service.aimodelpower.dto;
+
+import lombok.Data;
+
+/**
+ * 组织配置
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+public class OrgConfig {
+    /**
+     * id
+     */
+    private Long id;
+    /**
+     * 水厂id
+     */
+    private String orgId;
+    /**
+     * 水厂名称
+     */
+    private String orgName;
+}

+ 33 - 0
src/main/java/com/shkpr/service/aimodelpower/dto/ShizilaishuiHistory.java

@@ -0,0 +1,33 @@
+package com.shkpr.service.aimodelpower.dto;
+
+import lombok.Data;
+
+/**
+ * 市自来水历史表
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+public class ShizilaishuiHistory {
+    /**
+     * 采集标签
+     */
+    private String tagCode;
+    /**
+     * 水厂名称
+     */
+    private String name;
+    /**
+     * 时间
+     */
+    private String time;
+    /**
+     * 修改时间
+     */
+    private String updateTime;
+    /**
+     * 值
+     */
+    private Double val;
+}

+ 61 - 0
src/main/java/com/shkpr/service/aimodelpower/dto/WaterPumpCollectionConfig.java

@@ -0,0 +1,61 @@
+package com.shkpr.service.aimodelpower.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 泵采集配置表
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class WaterPumpCollectionConfig {
+    /**
+     * 自增id
+     */
+    private Long id;
+
+    /**
+     * 设施名称
+     */
+    private String facilitiesName;
+
+    /**
+     * 设备名称
+     */
+    private String serviceName;
+
+    /**
+     * 采集标签
+     */
+    private String collcationTag;
+
+    /**
+     * 设备编号
+     */
+    private String deviceCode;
+
+    /**
+     * 采集对应标准字段
+     */
+    private String standardCode;
+
+    /**
+     * 分区id(组织机构id)
+     */
+    private String zoneId;
+
+    /**
+     * 额定流量
+     */
+    private Double waterCapacity;
+
+    /**
+     * 功耗
+     */
+    private Double power;
+}

+ 41 - 0
src/main/java/com/shkpr/service/aimodelpower/dto/WaterPumpCollectionRecord.java

@@ -0,0 +1,41 @@
+package com.shkpr.service.aimodelpower.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 泵采集数据表
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class WaterPumpCollectionRecord {
+    /**
+     * 主键自增id
+     */
+    private Long id;
+    /**
+     * 设备编号
+     */
+    private String deviceCode;
+    /**
+     * 采集时间(矫正时间,该时间含义表示如为3点 则为2-3点的数据)
+     */
+    private String time;
+    /**
+     * 电量(累计电量读数)
+     */
+    private Double activeEnergy;
+    /**
+     * 启停状态 0 停 1 启
+     */
+    private Short startupState;
+    /**
+     * 小时用电量
+     */
+    private Double powerCons;
+}

+ 49 - 0
src/main/java/com/shkpr/service/aimodelpower/dto/WaterPumpEnergyConfig.java

@@ -0,0 +1,49 @@
+package com.shkpr.service.aimodelpower.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 泵额定流量配置表
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class WaterPumpEnergyConfig {
+    /**
+     * 主键ID
+     */
+    private Long id;
+    /**
+     * 泵号
+     */
+    private String pumpID;
+    /**
+     * 泵类型
+     */
+    private String pumpType;
+    /**
+     * 工频/变频
+     */
+    private String frequencyType;
+    /**
+     * 额定流量
+     */
+    private Double waterCapacity;
+    /**
+     * 每个设备的额定功率
+     */
+    private Double power;
+    /**
+     * 水厂ID
+     */
+    private String zoneId;
+    /**
+     * 水泵名称
+     */
+    private String pumpName;
+}

+ 41 - 0
src/main/java/com/shkpr/service/aimodelpower/dto/WaterVolumeCollectionConfig.java

@@ -0,0 +1,41 @@
+package com.shkpr.service.aimodelpower.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 水量采集配置表
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class WaterVolumeCollectionConfig {
+    /**
+     * 主键id
+     */
+    private Long id;
+
+    /**
+     * 组织机构名称(水厂名称)
+     */
+    private String orgName;
+
+    /**
+     * 设施名称
+     */
+    private String facilitiesName;
+
+    /**
+     * 设备名称
+     */
+    private String serviceName;
+
+    /**
+     * 采集标签
+     */
+    private String collcationTag;
+}

+ 56 - 0
src/main/java/com/shkpr/service/aimodelpower/dto/WaterVolumeCollectionRecord.java

@@ -0,0 +1,56 @@
+package com.shkpr.service.aimodelpower.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 水量采集数据表
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class WaterVolumeCollectionRecord {
+    /**
+     * 主键自增id
+     */
+    private Long id;
+    /**
+     * 水厂名称
+     */
+    private String orgName;
+    /**
+     * 采集时间
+     */
+    private String time;
+    /**
+     * 采集时间(对比整点矫正过后的采集时间)
+     */
+    private String updateTime;
+    /**
+     * 水量值
+     */
+    private String value;
+    /**
+     * 采集数据类型
+     */
+    private String valueTag;
+    /**
+     * 采集标识
+     */
+    private String collectionTag;
+    /**
+     * 水厂设备数组
+     */
+    private String collectionTagArray;
+
+    public WaterVolumeCollectionRecord(String orgName, String time, String valueTag, String collectionTagArray) {
+        this.orgName = orgName;
+        this.time = time;
+        this.valueTag = valueTag;
+        this.collectionTagArray = collectionTagArray;
+    }
+}

+ 69 - 0
src/main/java/com/shkpr/service/aimodelpower/dto/WaterVolumePredictHour.java

@@ -0,0 +1,69 @@
+package com.shkpr.service.aimodelpower.dto;
+
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+
+/**
+ * 水量预测小时表
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+public class WaterVolumePredictHour {
+    /**
+     * 主键自增id
+     */
+    private Long id;
+    /**
+     * 日期
+     */
+    private LocalDate date;
+    /**
+     * 小时
+     */
+    private LocalTime hour;
+    /**
+     * 小时预测取水量
+     */
+    private Double hourForecastWaterWithdrawals;
+    /**
+     * 小时预测供水量
+     */
+    private Double hourForecastActualWaterSupply;
+    /**
+     * 取水能耗
+     */
+    private Double waterWithdrawalsEnergy;
+    /**
+     * 供水能耗
+     */
+    private Double waterSupplyEnergy;
+    /**
+     * 实际取水能耗
+     */
+    private Double realWaterWithdrawalsEnergy;
+    /**
+     * 实际供水能耗
+     */
+    private Double realWaterSupplyEnergy;
+    /**
+     * 上次修改时间
+     */
+    private LocalDateTime lastModifyTime;
+    /**
+     * 水厂id
+     */
+    private String orgId;
+    /**
+     * 小时实际取水量
+     */
+    private Double hourActualWaterWithdrawals;
+    /**
+     * 小时实际供水量
+     */
+    private Double hourActualWaterSupply;
+}

文件差异内容过多而无法显示
+ 67 - 70
src/main/java/com/shkpr/service/aimodelpower/globalmgr/ScheduleTaskMgr.java


+ 29 - 0
src/main/java/com/shkpr/service/aimodelpower/jsonbean/ModelPredictorDay.java

@@ -0,0 +1,29 @@
+package com.shkpr.service.aimodelpower.jsonbean;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 模型预测日参数
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ModelPredictorDay {
+    /**
+     * 组织id
+     */
+    private String orgId;
+    /**
+     * 开始时间
+     */
+    private String sdate;
+    /**
+     * 结束时间
+     */
+    private String endate;
+}

+ 25 - 0
src/main/java/com/shkpr/service/aimodelpower/jsonbean/ModelPredictorHour.java

@@ -0,0 +1,25 @@
+package com.shkpr.service.aimodelpower.jsonbean;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 模型预测小时参数
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ModelPredictorHour {
+    /**
+     * 组织id
+     */
+    private String orgId;
+    /**
+     * 时间
+     */
+    private String date;
+}

+ 21 - 0
src/main/java/com/shkpr/service/aimodelpower/jsonbean/ModelPredictorTrain.java

@@ -0,0 +1,21 @@
+package com.shkpr.service.aimodelpower.jsonbean;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * 模型预测训练参数
+ *
+ * @author 欧阳劲驰
+ * @serial 1.0.4
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ModelPredictorTrain {
+    /**
+     * 组织id
+     */
+    private String orgId;
+}

文件差异内容过多而无法显示
+ 55 - 36
src/main/resources/application.properties


+ 8 - 0
src/main/resources/mapper/OrgConfigMapper.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.shkpr.service.aimodelpower.dbdao.mapperwatervolume.OrgConfigMapper">
+    <select id="findAll" resultType="com.shkpr.service.aimodelpower.dto.OrgConfig">
+        select org_name, org_id, id
+        from water_org_config
+    </select>
+</mapper>

+ 129 - 0
src/main/resources/mapper/ShizilaishuiHistoryMapper.xml

@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.shkpr.service.aimodelpower.dbdao.mapperoracle.ShizilaishuiHistoryMapper">
+    <select id="findDifferenceByTimeAndTags" resultType="com.shkpr.service.aimodelpower.dto.ShizilaishuiHistory"
+            fetchSize="3000">
+        with time_seq (start_time, end_time)
+        as (select to_date(#{beginTime,jdbcType=VARCHAR}, 'yyyy-mm-dd hh24:mi:ss') as start_time,
+        to_date(#{beginTime,jdbcType=VARCHAR}, 'yyyy-mm-dd hh24:mi:ss')
+        + numtodsinterval(#{interval,jdbcType=VARCHAR}, #{intervalUnit,jdbcType=VARCHAR})
+        + numtodsinterval('1', 'minute') as end_time
+        from dual
+        union all
+        select
+        start_time + numtodsinterval(#{interval,jdbcType=VARCHAR}, #{intervalUnit,jdbcType=VARCHAR}),
+        end_time + numtodsinterval(#{interval,jdbcType=VARCHAR}, #{intervalUnit,jdbcType=VARCHAR})
+        from time_seq
+        where start_time +
+        numtodsinterval(#{interval,jdbcType=VARCHAR}, #{intervalUnit,jdbcType=VARCHAR}) &lt;
+        to_date(#{endTime,jdbcType=VARCHAR}, 'yyyy-mm-dd hh24:mi:ss'))
+        select ts.start_time as time,
+        <!--自供-->
+        nvl((select sum(water_usage)
+        from (select abs(
+        max(val) keep (dense_rank last order by qcquisition_time) -
+        max(val) keep (dense_rank first order by qcquisition_time)
+        ) as water_usage,
+        tag_code
+        from cqda.v_shizilaishui_history2
+        where tag_code in
+        <foreach collection="selfTags" open="(" close=")" separator="," item="tag">
+            #{tag,jdbcType=VARCHAR}
+        </foreach>
+        and qcquisition_time &gt;= ts.start_time
+        and qcquisition_time &lt; ts.end_time
+        group by tag_code
+        having count(*) &gt; 1)), 0)
+        <!--供入-->
+        <if test="inTags!=null and inTags.size > 0">
+            + nvl((select sum(water_usage)
+            from (select abs(
+            max(val) keep (dense_rank last order by qcquisition_time) -
+            max(val) keep (dense_rank first order by qcquisition_time)
+            ) as water_usage,
+            tag_code
+            from cqda.v_shizilaishui_history2
+            where tag_code in
+            <foreach collection="inTags" open="(" close=")" separator="," item="tag">
+                #{tag,jdbcType=VARCHAR}
+            </foreach>
+            and qcquisition_time &gt;= ts.start_time
+            and qcquisition_time &lt; ts.end_time
+            group by tag_code
+            having count(*) &gt; 1)), 0)
+        </if>
+        <!--供出-->
+        <if test="outTags!=null and outTags.size > 0">
+            -
+            nvl((select sum(water_usage)
+            from (select abs(
+            max(val) keep (dense_rank last order by qcquisition_time) -
+            max(val) keep (dense_rank first order by qcquisition_time)
+            ) as water_usage,
+            tag_code
+            from cqda.v_shizilaishui_history2
+            where tag_code in
+            <foreach collection="outTags" open="(" close=")" separator="," item="tag">
+                #{tag,jdbcType=VARCHAR}
+            </foreach>
+            and qcquisition_time &gt;= ts.start_time
+            and qcquisition_time &lt; ts.end_time
+            group by tag_code
+            having count(*) &gt; 1)), 0)
+        </if>
+        as val
+        from time_seq ts
+        order by ts.start_time
+    </select>
+
+    <select id="findAlignByTimeAndTags" resultType="com.shkpr.service.aimodelpower.dto.ShizilaishuiHistory" fetchSize="3000">
+        with time_seq (start_time)
+        as (select to_date(#{beginTime,jdbcType=VARCHAR}, 'yyyy-mm-dd hh24:mi:ss') as start_time
+        from dual
+        union all
+        select start_time + numtodsinterval(#{interval,jdbcType=VARCHAR}, #{intervalUnit,jdbcType=VARCHAR})
+        from time_seq
+        where start_time +
+        numtodsinterval(#{interval,jdbcType=VARCHAR}, #{intervalUnit,jdbcType=VARCHAR}) &lt;
+        to_date(#{endTime,jdbcType=VARCHAR}, 'yyyy-mm-dd hh24:mi:ss'))
+        select ts.start_time as time,sh.val,sh.tag_code
+        from time_seq ts
+        left join (select tag_code, val,trunc(qcquisition_time, #{alignUnit,jdbcType=VARCHAR}) as hour_trunc,
+        row_number() over (partition by trunc(qcquisition_time, #{alignUnit,jdbcType=VARCHAR}), tag_code
+        order by abs(qcquisition_time - trunc(qcquisition_time, #{alignUnit,jdbcType=VARCHAR}))) as rn
+        from cqda.v_shizilaishui_history2
+        where tag_code in
+        <foreach collection="selfTags" open="(" close=")" separator="," item="tag">
+            #{tag,jdbcType=VARCHAR}
+        </foreach>
+        and qcquisition_time &gt;= to_date(#{beginTime,jdbcType=VARCHAR}, 'yyyy-mm-dd hh24:mi:ss')
+        and qcquisition_time &lt; to_date(#{endTime,jdbcType=VARCHAR}, 'yyyy-mm-dd hh24:mi:ss')) sh
+        on sh.hour_trunc = ts.start_time and sh.rn = 1
+        order by ts.start_time
+    </select>
+
+    <select id="findAllByTimeAndTags" resultType="com.shkpr.service.aimodelpower.dto.ShizilaishuiHistory" fetchSize="3000">
+        select tag_code,
+        name,
+        val,
+        to_char(qcquisition_time, 'yyyy-mm-dd hh24:mi:ss') as time,
+        to_char(update_time, 'yyyy-mm-dd hh24:mi:ss') as update_time
+        from cqda.v_shizilaishui_history2
+        where tag_code in
+        <foreach collection="selfTags" open="(" close=")" separator="," item="tag">
+            #{tag,jdbcType=VARCHAR}
+        </foreach>
+        and qcquisition_time &gt;= to_date(#{beginTime,jdbcType=VARCHAR}, 'yyyy-mm-dd hh24:mi:ss')
+        and qcquisition_time &lt; to_date(#{endTime,jdbcType=VARCHAR}, 'yyyy-mm-dd hh24:mi:ss')
+    </select>
+
+    <select id="findCountByTimeAndTags" resultType="java.lang.Long">
+        select count(1) from cqda.v_shizilaishui_history2
+        where tag_code in
+        <foreach collection="selfTags" open="(" close=")" separator="," item="tag">
+            #{tag,jdbcType=VARCHAR}
+        </foreach>
+        and qcquisition_time &gt;= to_date(#{beginTime,jdbcType=VARCHAR}, 'yyyy-mm-dd hh24:mi:ss')
+        and qcquisition_time &lt; to_date(#{endTime,jdbcType=VARCHAR}, 'yyyy-mm-dd hh24:mi:ss')
+    </select>
+</mapper>

+ 28 - 0
src/main/resources/mapper/WaterPumpCollectionConfigMapper.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.shkpr.service.aimodelpower.dbdao.mapperwatervolume.WaterPumpCollectionConfigMapper">
+    <select id="findAll" resultType="com.shkpr.service.aimodelpower.dto.WaterPumpCollectionConfig">
+        select id,
+               facilities_name,
+               service_name,
+               collcation_tag,
+               device_code,
+               standard_code,
+               zone_id,
+               "WaterCapacity" as water_capacity,
+               "Power"         as power
+        from water_pump_collection_config
+    </select>
+
+    <select id="findEnergy" resultType="com.shkpr.service.aimodelpower.dto.WaterPumpEnergyConfig">
+        select "ID"            as id,
+               "PumpID"        as pump_id,
+               "PumpType"      as pump_type,
+               "FrequencyType" as frequency_type,
+               "WaterCapacity" as water_capacity,
+               "Power"         as power,
+               zone_id,
+               "PumpName"      as pump_name
+        from tb_m_pump_energy
+    </select>
+</mapper>

+ 24 - 0
src/main/resources/mapper/WaterPumpCollectionRecordMapper.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.shkpr.service.aimodelpower.dbdao.mapperwatervolume.WaterPumpCollectionRecordMapper">
+    <insert id="upsert">
+        insert into water_pump_record_all (device_code, time, active_energy, startup_state, power_cons)
+        values (#{deviceCode,jdbcType=VARCHAR},
+                #{time,jdbcType=VARCHAR},
+                #{activeEnergy,jdbcType=DOUBLE},
+                #{startupState,jdbcType=SMALLINT},
+                #{powerCons,jdbcType=DOUBLE})
+        on conflict (device_code, time) do update
+            set active_energy = excluded.active_energy,
+                startup_state = excluded.startup_state,
+                power_cons    = excluded.power_cons;
+    </insert>
+
+    <select id="findAllByTimeAndDeviceCode" resultType="com.shkpr.service.aimodelpower.dto.WaterPumpCollectionRecord" fetchSize="3000">
+        select id, device_code, time, active_energy, startup_state, power_cons
+        from water_pump_record_all
+        where device_code = #{deviceCode,jdbcType=VARCHAR}
+          and time::timestamp &gt;= #{beginTime,jdbcType=VARCHAR}::timestamp
+          and time::timestamp &lt; #{endTime,jdbcType=VARCHAR}::timestamp
+    </select>
+</mapper>

+ 8 - 0
src/main/resources/mapper/WaterVolumeCollectionConfigMapper.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.shkpr.service.aimodelpower.dbdao.mapperwatervolume.WaterVolumeCollectionConfigMapper">
+    <select id="findAll" resultType="com.shkpr.service.aimodelpower.dto.WaterVolumeCollectionConfig">
+        select id, org_name, facilities_name, service_name, collcation_tag
+        from water_collection_config
+    </select>
+</mapper>

+ 57 - 0
src/main/resources/mapper/WaterVolumeCollectionRecordMapper.xml

@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.shkpr.service.aimodelpower.dbdao.mapperwatervolume.WaterVolumeCollectionRecordMapper">
+    <insert id="upsertRaw">
+        insert into water_collecation_record (org_name, time, update_time, value, collcation_tag)
+        values (#{orgName,jdbcType=VARCHAR},
+                #{time,jdbcType=VARCHAR}::int8,
+                #{updateTime,jdbcType=VARCHAR}::int8,
+                #{value,jdbcType=VARCHAR},
+                #{collectionTag,jdbcType=VARCHAR})
+        on conflict (org_name,time, collcation_tag) do update set value = excluded.value;
+    </insert>
+
+    <insert id="upsertHour">
+        insert into water_collecation_record_all (org_name, time, value, value_tag, collcation_tag_array)
+        values (#{orgName,jdbcType=VARCHAR},
+                #{time,jdbcType=VARCHAR},
+                #{value,jdbcType=VARCHAR},
+                #{valueTag,jdbcType=VARCHAR},
+                #{collectionTagArray,jdbcType=VARCHAR})
+        on conflict (org_name,time, value_tag) do update set value = excluded.value;
+    </insert>
+
+    <insert id="upsertMinute">
+        insert into water_collecation_record_all_new (org_name, time, value, value_tag, collcation_tag_array)
+        values (#{orgName,jdbcType=VARCHAR},
+                #{time,jdbcType=VARCHAR},
+                #{value,jdbcType=VARCHAR},
+                #{valueTag,jdbcType=VARCHAR},
+                #{collectionTagArray,jdbcType=VARCHAR})
+        on conflict (org_name,time, value_tag) do update set value = excluded.value;
+    </insert>
+
+    <select id="findLastValueHour" resultType="java.lang.Double">
+        select value::float8
+        from water_collecation_record_all
+        where org_name = #{orgName,jdbcType=VARCHAR}
+          and time::timestamp &lt; #{time,jdbcType=VARCHAR}::timestamp
+          and value_tag = #{valueTag,jdbcType=VARCHAR}
+          and "value"::float8 &gt; #{minValue,jdbcType=VARCHAR}
+          and "value"::float8 &lt; #{maxValue,jdbcType=VARCHAR}
+        order by time desc
+        limit 1
+    </select>
+
+    <select id="findLastValueMinute" resultType="java.lang.Double">
+        select value::float8
+        from water_collecation_record_all_new
+        where org_name = #{orgName,jdbcType=VARCHAR}
+          and time::timestamp &lt; #{time,jdbcType=VARCHAR}::timestamp
+          and value_tag = #{valueTag,jdbcType=VARCHAR}
+          and "value"::float8 &gt; #{minValue,jdbcType=VARCHAR}
+          and "value"::float8 &lt; #{maxValue,jdbcType=VARCHAR}
+        order by time desc
+        limit 1
+    </select>
+</mapper>

+ 73 - 0
src/main/resources/mapper/WaterVolumePredictHourMapper.xml

@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.shkpr.service.aimodelpower.dbdao.mapperwatervolume.WaterVolumePredictHourMapper">
+    <insert id="upsertMinute">
+        INSERT INTO tb_m_hourwater_new ("Date",
+                                        "Hour",
+                                        "orgId",
+                                        "HourForecastWaterWithdrawals",
+                                        "HourForecastActualWaterSupply",
+                                        "WaterWithdrawalsEnergy",
+                                        "WaterSupplyEnergy",
+                                        "RealWaterWithdrawalsEnergy",
+                                        "RealWaterSupplyEnergy",
+                                        "LastModifyTime",
+                                        "HourActualWaterWithdrawals",
+                                        "HourActualWaterSupply")
+        VALUES (#{date,jdbcType=DATE},
+                #{hour,jdbcType=TIME},
+                #{orgId,jdbcType=VARCHAR},
+                #{hourForecastWaterWithdrawals,jdbcType=DOUBLE},
+                #{hourForecastActualWaterSupply,jdbcType=DOUBLE},
+                #{waterWithdrawalsEnergy,jdbcType=DOUBLE},
+                #{waterSupplyEnergy,jdbcType=DOUBLE},
+                #{realWaterWithdrawalsEnergy,jdbcType=DOUBLE},
+                #{realWaterSupplyEnergy,jdbcType=DOUBLE},
+                #{lastModifyTime,jdbcType=TIMESTAMP},
+                #{hourActualWaterWithdrawals,jdbcType=DOUBLE},
+                #{hourActualWaterSupply,jdbcType=DOUBLE})
+        ON CONFLICT ("Date", "Hour", "orgId")
+            DO UPDATE SET "HourForecastWaterWithdrawals"  = EXCLUDED."HourForecastWaterWithdrawals",
+                          "HourForecastActualWaterSupply" = EXCLUDED."HourForecastActualWaterSupply",
+                          "WaterWithdrawalsEnergy"        = EXCLUDED."WaterWithdrawalsEnergy",
+                          "WaterSupplyEnergy"             = EXCLUDED."WaterSupplyEnergy",
+                          "RealWaterWithdrawalsEnergy"    = EXCLUDED."RealWaterWithdrawalsEnergy",
+                          "RealWaterSupplyEnergy"         = EXCLUDED."RealWaterSupplyEnergy",
+                          "LastModifyTime"                = EXCLUDED."LastModifyTime",
+                          "HourActualWaterWithdrawals"    = EXCLUDED."HourActualWaterWithdrawals",
+                          "HourActualWaterSupply"         = EXCLUDED."HourActualWaterSupply"
+    </insert>
+
+    <update id="syncStatusAndEnergyByTime">
+        update tb_m_hourwater_watersupply hw
+        set "RealPumpStatus" = pra.startup_state,
+            "RealPumpEnergy" = pra.power_cons
+        from water_pump_record_all pra
+        where hw."Date"::date = pra.time::date
+          and hw."Hour"::time = pra.time::time
+          and hw."PumpID" = pra.device_code
+          and pra.time::timestamp &gt;= #{beginDate,jdbcType=VARCHAR}::timestamp
+          and pra.time::timestamp &lt; #{endDate,jdbcType=VARCHAR}::timestamp
+    </update>
+
+    <select id="findHourByTimeAndOrgId" resultType="com.shkpr.service.aimodelpower.dto.WaterVolumePredictHour"
+            fetchSize="3000">
+        SELECT "ID"                            AS id,
+               "Date"                          AS date,
+               "Hour"                          AS hour,
+               "HourForecastWaterWithdrawals"  AS hour_forecast_water_withdrawals,
+               "HourForecastActualWaterSupply" AS hour_forecast_actual_water_supply,
+               "WaterWithdrawalsEnergy"        AS water_withdrawals_energy,
+               "WaterSupplyEnergy"             AS water_supply_energy,
+               "RealWaterWithdrawalsEnergy"    AS real_water_withdrawals_energy,
+               "RealWaterSupplyEnergy"         AS real_water_supply_energy,
+               "LastModifyTime"                AS last_modify_time,
+               "orgId"                         AS org_id,
+               "HourActualWaterWithdrawals"    AS hour_actual_water_withdrawals,
+               "HourActualWaterSupply"         AS hour_actual_water_supply
+        FROM tb_m_hourwater
+        where "orgId" = #{orgId,jdbcType=VARCHAR}
+          and "Date"::date &gt;= #{beginDate,jdbcType=VARCHAR}::date
+          and "Date"::date &lt; #{endDate,jdbcType=VARCHAR}::date
+    </select>
+</mapper>

+ 13 - 1
src/test/java/com/shkpr/service/aimodelpower/AimodelpowerApplicationTests.java

@@ -1,8 +1,20 @@
 package com.shkpr.service.aimodelpower;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
 
+/**
+ * 测试启动类
+ */
 @SpringBootTest
-class AimodelpowerApplicationTests {
+@RunWith(SpringRunner.class)
+public class AimodelpowerApplicationTests {
+
+    @Test
+    public void test() {
+
+    }
 
 }