Переглянути джерело

增加调控下发定时执行

xusl 8 місяців тому
батько
коміт
aab681de89

+ 38 - 0
cpp-admin/src/main/java/com/cpp/CppApplication.java

@@ -1,10 +1,17 @@
 package com.cpp;
 
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.app.VelocityEngine;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.scheduling.TaskScheduler;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+
+import java.util.Properties;
 
 /**
  * 启动程序
@@ -21,4 +28,35 @@ public class CppApplication
         SpringApplication.run(CppApplication.class, args);
         System.out.println("(♥◠‿◠)ノ゙  集中预测启动成功   ლ(´ڡ`ლ)゙  \n");
     }
+
+    /**
+     * 模板引擎
+     *
+     * @return 返回模板引擎
+     */
+    @Bean
+    public VelocityEngine velocityEngine() {
+        VelocityEngine ve = new VelocityEngine();
+        Properties properties = new Properties();
+        //设置velocity资源加载方式为file
+        properties.setProperty("resource.loader", "file");
+        //设置velocity资源加载方式为file时的处理类
+        properties
+                .setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
+        properties.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, "");
+        properties.setProperty(Velocity.ENCODING_DEFAULT, "UTF-8");
+        properties.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
+        ve.init(properties);
+        return ve;
+    }
+
+    @Bean
+    public TaskScheduler taskScheduler(){
+        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
+        // 线程池大小
+        taskScheduler.setPoolSize(20);
+        // 线程名字的前缀
+        taskScheduler.setThreadNamePrefix("taskScheduler");
+        return taskScheduler;
+    }
 }

+ 5 - 5
cpp-admin/src/main/java/com/cpp/web/core/config/sharding/ShardingTablesLoadRunner.java

@@ -19,6 +19,7 @@ import org.springframework.boot.CommandLineRunner;
 import org.springframework.core.annotation.Order;
 import org.springframework.stereotype.Component;
 
+import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
 import java.time.LocalDateTime;
 import java.util.Date;
@@ -31,10 +32,10 @@ import java.util.Date;
  * @date 2022/12/20 15:41
  */
 @Slf4j
-@Order(value = 1) // 数字越小,越先执行
+@Order(1) // 数字越小,越先执行
 @Component
 @RequiredArgsConstructor
-public class ShardingTablesLoadRunner implements CommandLineRunner {
+public class ShardingTablesLoadRunner{
 
 
     private final ForecastPowerUltraShortTermRegulationService forecastPowerUltraShortTermRegulationService;
@@ -54,9 +55,8 @@ public class ShardingTablesLoadRunner implements CommandLineRunner {
     private final ForecastPowerShortTermSendService forecastPowerShortTermSendService;
 
 
-
-    @Override
-    public void run(String... args) {
+    @PostConstruct
+    public void run() {
         // 读取已有分表,进行缓存
         LambdaQueryWrapper<ForecastPowerUltraShortTermRegulation> forecastPowerUltraShortTermRegulationLambdaQueryWrapper = new LambdaQueryWrapper<>();
         forecastPowerUltraShortTermRegulationLambdaQueryWrapper.eq(ForecastPowerUltraShortTermRegulation::getTime, new Date()).last("limit 1");

+ 2 - 1
cpp-admin/src/main/java/com/cpp/web/domain/enums/RegulationStatusEnum.java

@@ -13,7 +13,8 @@ import lombok.Getter;
 @AllArgsConstructor
 public enum RegulationStatusEnum {
     E1(1, "已下发"),
-    E2(2, "未下发");
+    E2(2, "未下发"),
+    E3(3, "下发失败");
 
     private Integer code;
     private String message;

+ 1 - 1
cpp-admin/src/main/java/com/cpp/web/domain/regulation/TempShortRegulation.java

@@ -43,7 +43,7 @@ public class TempShortRegulation extends BaseCppEntity {
     private String createBy;
 
     /**
-     * 调控下发状态 E1:已下发,E2:未下发
+     * 调控下发状态 E1:已下发,E2:未下发,E3:下发失败
      */
     private String regulationStatusEnum;
     /**

+ 5 - 0
cpp-admin/src/main/java/com/cpp/web/domain/station/ElectricField.java

@@ -60,6 +60,11 @@ public class ElectricField extends BaseCppEntity {
     private String electricFieldTypeEnum;
 
     /**
+     * 场站标识
+     */
+    private String  stationSign;
+
+    /**
      * 上报省调
      */
 //    @Enumerated(EnumType.STRING)

+ 209 - 0
cpp-admin/src/main/java/com/cpp/web/job/RgulationSendJob.java

@@ -0,0 +1,209 @@
+package com.cpp.web.job;
+
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.ssh.Sftp;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import com.cpp.system.service.ISysConfigService;
+
+import com.cpp.web.domain.datafactory.SftpChannel;
+import com.cpp.web.domain.enums.RegulationStatusEnum;
+import com.cpp.web.domain.regulation.TempShortRegulation;
+import com.cpp.web.domain.regulation.TempShortRegulationDetail;
+import com.cpp.web.domain.station.*;
+import com.cpp.web.service.datafactory.SftpChannelService;
+import com.cpp.web.service.regulation.TempShortRegulationDetailService;
+import com.cpp.web.service.regulation.TempShortRegulationService;
+import com.cpp.web.service.station.ElectricFieldService;
+import com.cpp.web.service.station.ForecastPowerShortTermStationService;
+import com.cpp.web.utils.CppFileUtil;
+import com.cpp.web.utils.DateTimeUtil;
+import com.cpp.web.utils.SftpUtil;
+import com.cpp.web.utils.sftp.SftpTool;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateFormatUtils;
+import org.apache.commons.lang3.time.DateUtils;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.annotation.Order;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 定时任务管理类
+ */
+@Component
+@Order(9)
+@Slf4j
+public class RgulationSendJob {
+    @Autowired
+    ISysConfigService iSysConfigService;
+    @Autowired
+    ForecastPowerShortTermStationService forecastPowerShortTermStationService;
+    @Autowired
+    TempShortRegulationService tempShortRegulationService;
+    @Autowired
+    TempShortRegulationDetailService tempShortRegulationDetailService;
+    @Autowired
+    VelocityEngine velocityEngine;
+    @Autowired
+    ElectricFieldService electricFieldService;
+    @Autowired
+    SftpChannelService sftpChannelService;
+
+    /**
+     * 生成调控下发文件定时任务
+     */
+    @Scheduled(fixedDelay = 60000L)
+    public void createRegulationFile() {
+        try {
+            // 判断调控截止时间
+            String dqEndTime = iSysConfigService.selectConfigByKey("dqEndTime");
+            if (!StrUtil.isBlankIfStr(dqEndTime)) {
+                Date tkDate = DateTimeUtil.getDayStartTime(System.currentTimeMillis());
+                QueryWrapper<TempShortRegulation> tempShortRegulationQueryWrapper = new QueryWrapper<>();
+                tempShortRegulationQueryWrapper.eq("tk_date", tkDate);
+                tempShortRegulationQueryWrapper.eq("regulation_status_enum", RegulationStatusEnum.E2.name());
+                // 查找调控主表当日未下发的记录
+                List<TempShortRegulation> tempShortRegulationList = tempShortRegulationService.list(tempShortRegulationQueryWrapper);
+                // 存在未下发的记录就执行下发任务
+                if (!tempShortRegulationList.isEmpty()){
+                    // 生成截止时间
+                    Date dqEndDate = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd " + dqEndTime), "yyyy-MM-dd HH:mm");
+                    if (new Date().before(dqEndDate)) {
+                        // 超过截止时间就执行调控下发,获取当天将要下发的调控记录
+                        QueryWrapper<TempShortRegulationDetail> tempShortRegulationDetailQueryWrapper = new QueryWrapper<>();
+                        // 当日调控
+                        tempShortRegulationDetailQueryWrapper.eq("tk_date", tkDate);
+                        List<TempShortRegulationDetail> tempShortRegulationDetailList = tempShortRegulationDetailService.list(tempShortRegulationDetailQueryWrapper);
+                        if (!tempShortRegulationDetailList.isEmpty()) {
+                            // 按场站编号分组
+                            Map<String, List<TempShortRegulationDetail>> tempShortRegulationDetailGroup = tempShortRegulationDetailList.stream().collect(Collectors.groupingBy(s -> s.getStationCode()));
+                            // 统计出需要调控的场站编号
+                            List<String> stationCodeList = new ArrayList<>(tempShortRegulationDetailGroup.keySet());
+                            // 获取场站原始值
+                            QueryWrapper<ForecastPowerShortTermStation> forecastPowerShortTermStationQueryWrapper = new QueryWrapper<>();
+                            forecastPowerShortTermStationQueryWrapper.eq("gen_date", DateTimeUtil.getDayStartTime(System.currentTimeMillis()));
+                            forecastPowerShortTermStationQueryWrapper.and(wrapper ->
+                                    stationCodeList.stream()
+                                            .map(code -> wrapper.eq("station_code", code).or())
+                                            .reduce((a, b) -> b)
+                                            .get()
+                            );
+                            List<ForecastPowerShortTermStation> forecastPowerShortTermStationList = forecastPowerShortTermStationService.list(forecastPowerShortTermStationQueryWrapper);
+                            // 获取所有场站信息
+                            List<ElectricField> electricFieldList = electricFieldService.list();
+                            // 获取所有sftp通道信息
+                            List<SftpChannel> sftpChannelList = sftpChannelService.list();
+                            // 循环场站生成调控文件
+                            for (String stationCode : stationCodeList) {
+                                String vmFileName = "";
+                                // 定义下发状态
+                                String isSend = RegulationStatusEnum.E3.name();
+                                try {
+                                    List<TempShortRegulationDetail> shortRegulationDetailList = tempShortRegulationDetailGroup.get(stationCode);
+                                    Map<Long, BigDecimal> tkMap = new HashMap<>();
+                                    for (TempShortRegulationDetail tempShortRegulationDetail : shortRegulationDetailList) {
+                                        tkMap.put(tempShortRegulationDetail.getTime().getTime(), tempShortRegulationDetail.getTkValue());
+                                    }
+
+                                    // 获取该场站的原始值
+                                    List<ForecastPowerShortTermStation> tempStationList = forecastPowerShortTermStationList.stream().filter(s -> s.getStationCode().equals(stationCode)).collect(Collectors.toList());
+                                    tempStationList.sort(Comparator.comparing(ForecastPowerShortTermStation::getTime));
+                                    for (ForecastPowerShortTermStation forecastPowerShortTermStation : tempStationList) {
+                                        // 遍历原始值每个点位,如果该点位存在调控值,则替换原始值。
+                                        if (tkMap.get(forecastPowerShortTermStation.getTime().getTime()) != null) {
+                                            forecastPowerShortTermStation.setFpValue(tkMap.get(forecastPowerShortTermStation.getTime().getTime()));
+                                        }
+                                    }
+                                    // 获取该场站信息
+                                    ElectricField electricField = electricFieldList.stream().filter(s -> s.getStationCode().equals(stationCode)).findFirst().get();
+
+                                    // 生成调控文件
+                                    VelocityContext velocityContext = new VelocityContext();
+                                    velocityContext.put("vList", tempStationList);
+                                    velocityContext.put("stationSign", electricField.getStationSign());
+                                    velocityContext.put("capacity", electricField.getCapacity());
+                                    velocityContext.put("firstTime", DateFormatUtils.format(tempStationList.get(0).getTime(), "yyyy-MM-dd_HH:mm"));
+                                    // 系统当前日期
+                                    velocityContext.put("currentTime", DateFormatUtils.format(System.currentTimeMillis(), "yyyy-MM-dd_HH:mm:ss"));
+                                    StringWriter writer = new StringWriter();
+                                    org.apache.velocity.Template template = this.velocityEngine.getTemplate(CppFileUtil.getResourceBasePath() + "/templates/send_dq.vm");
+                                    template.merge(velocityContext, writer);
+                                    vmFileName = electricField.getStationSign() + "_" + DateFormatUtils.format(tempStationList.get(0).getTime(), "yyyyMMdd_HHmm") + "_DQ.WPD";
+                                    // 获取临时存储文件路径
+                                    String tempCreateFilePath = CppFileUtil.getTempCreateFilePath();
+                                    File dirFile = FileUtil.mkdir(tempCreateFilePath);
+                                    if (dirFile==null) {
+                                        throw new RuntimeException("创建文件夹tempCreateFile失败");
+                                    }
+
+                                    createSendDqFile(writer, new File(tempCreateFilePath + File.separator + vmFileName));
+                                    SftpChannel sftpChannel = sftpChannelList.stream().filter(s -> s.getId().longValue() == electricField.getSftpChanelId().longValue()).findFirst().get();
+                                    SftpTool sftpTool = SftpUtil.createSftp(sftpChannel);
+                                    Sftp sftp = sftpTool.getSftp();
+                                    try (InputStream inputStream = new FileInputStream(tempCreateFilePath + File.separator + vmFileName)) {
+                                        sftp.getClient().put(inputStream, electricField.getSftpSendUrl() + "/" + vmFileName);
+                                    } finally {
+                                        sftpTool.close();
+                                    }
+                                    isSend = RegulationStatusEnum.E1.name();
+                                } catch (Exception e) {
+                                    log.error(stationCode + "生成调控下发文件定时任务失败", e);
+                                }
+                                finally {
+                                    // 更新调控主表下发状态
+                                    UpdateWrapper<TempShortRegulation> regulationUpdateWrapper = new UpdateWrapper<>();
+                                    regulationUpdateWrapper.eq("station_code",stationCode).eq("tk_date",tkDate).set("regulation_status_enum",isSend);
+                                    tempShortRegulationService.update(regulationUpdateWrapper);
+                                    // 删除本地文件
+                                    if (!StrUtil.isBlankIfStr(vmFileName)) {
+                                        FileUtil.del(CppFileUtil.getTempCreateFilePath() + File.separator + vmFileName);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            log.error("生成调控下发文件定时任务失败", e);
+        }
+    }
+
+    /**
+     * 生成上报文件
+     *
+     * @param writer
+     * @param file
+     */
+    protected void createSendDqFile(StringWriter writer, File file) {
+        FileOutputStream os = null;
+        try {
+            os = new FileOutputStream(file);
+            // 采用UTF-8字符集
+            os.write(writer.toString().getBytes("UTF-8"));
+            os.flush();
+
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            if (os != null) {
+                try {
+                    os.close();
+                } catch (IOException e) {
+                    log.error("文件生成关闭流失败", e);
+                }
+            }
+        }
+    }
+}

+ 54 - 0
cpp-admin/src/main/java/com/cpp/web/utils/CppFileUtil.java

@@ -0,0 +1,54 @@
+package com.cpp.web.utils;
+
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.ResourceUtils;
+
+import java.io.*;
+import java.net.URLDecoder;
+
+/**
+ * 操作文件工具类
+ *
+ * @author tl
+ * @date 2022-05-11 10:08:21
+ */
+@Slf4j
+public class CppFileUtil {
+    /**
+     * 获取项目根路径
+     *
+     * @return
+     */
+    public static String getResourceBasePath() {
+        // 获取跟目录
+        File path = null;
+        try {
+            path = new File(ResourceUtils.getURL("classpath:").getPath());
+
+        } catch (FileNotFoundException e) {
+            // nothing to do
+        }
+        if (path == null || !path.exists()) {
+            path = new File("");
+        }
+
+        String pathStr = path.getAbsolutePath();
+        try {
+            pathStr = URLDecoder.decode(pathStr, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        }
+        // 如果是在eclipse中运行,则和target同级目录,如果是jar部署到服务器,则默认和jar包同级
+//    pathStr = pathStr.replace("\\target\\classes", "");
+
+        return pathStr;
+    }
+
+    public static String getTempCreateFilePath() {
+        String path = "/home/syjy/cpp/tempCreateFile";
+        return path;
+    }
+
+
+}

+ 14 - 0
cpp-admin/src/main/resources/templates/send_dq.vm

@@ -0,0 +1,14 @@
+// ${currentTime}
+<! Entity=${stationSign}  type=DQ time='${firstTime}' !>
+
+<Capacity::${stationSign}>
+@id   装机容量
+#1    ${capacity}
+</Capacity::${stationSign}>
+
+<ShortTermForcast::${stationSign}>
+@id 短期预测功率 预计开机容量
+#foreach( $dq in $vList )
+#$foreach.count   ${dq.fpValue}   ${dq.openCapacity}
+#end
+</ShortTermForcast::${stationSign}>

+ 17 - 1
cpp-ui/src/views/configManager/electricField/index.vue

@@ -75,6 +75,7 @@
         <vxe-table-column field="electricFieldTypeEnum" title="类型"
                           :formatter="electricFieldTypeFormat"></vxe-table-column>
         <vxe-table-column field="capacity" title="装机容量(MW)"></vxe-table-column>
+        <vxe-table-column field="stationSign" title="场站标识"></vxe-table-column>
         <vxe-table-column field="longitude" title="经度"></vxe-table-column>
         <vxe-table-column field="latitude" title="纬度"></vxe-table-column>
         <vxe-table-column field="provinceEnum" title="省份" :formatter="provinceEnumFormat"></vxe-table-column>
@@ -156,6 +157,13 @@
               </el-form-item>
             </el-col>
             <el-col :span="12">
+              <el-form-item label="场站标识" prop="stationSign">
+                <el-input style="width: 100%" v-model="form.stationSign" maxlength="50"/>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row class="mb4">
+            <el-col :span="12">
               <el-form-item label="ftp通道名称">
                 <el-select v-model="form.sftpChanelId" placeholder="请选择" style="width: 100%" clearable
                            @change="ftpChannelChange" popper-class="cpp-popper">
@@ -168,6 +176,9 @@
                 </el-select>
               </el-form-item>
             </el-col>
+            <el-col :span="12">
+
+            </el-col>
           </el-row>
           <el-row class="mb4">
             <el-col :span="24">
@@ -221,7 +232,8 @@ export default {
         sftpChanelId: '',
         sftpUrl: '',
         sftpBackupUrl: '',
-        sftpSendUrl: ''
+        sftpSendUrl: '',
+        stationSign:'',
       },
       title: "",
       // 是否显示弹出层
@@ -250,6 +262,9 @@ export default {
         name: [
           {required: true, message: "场站名称不能为空", trigger: "blur"}
         ],
+        stationSign: [
+          {required: true, message: "场站标识不能为空", trigger: "blur"}
+        ],
         electricFieldTypeEnum: [
           {required: true, message: "类型不能为空", trigger: "blur"}
         ],
@@ -324,6 +339,7 @@ export default {
         provinceEnum: '',
         ftpChanelId: '',
         ftpUrl: '',
+        stationSign:'',
       },
         this.resetForm("form");
     },