From b54df8b5f80d32813b96a9f4da7ced8b82c284d0 Mon Sep 17 00:00:00 2001 From: hekaiyu <13673834656@163.com> Date: Thu, 30 Oct 2025 16:01:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E3=80=81=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E3=80=81=E8=AE=B0=E5=BD=95=E3=80=81=E6=98=BE=E7=A4=BAT1H?= =?UTF-8?q?=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../constant/WeatherPrefixConstants.java | 1 + .../constant/enums/T1hFilePrefixEnum.java | 51 +++ .../constant/enums/WeatherDataSourceEnum.java | 7 +- .../constant/enums/WeatherFileSuffixEnum.java | 2 +- .../enums/WeatherVariableNameEnum.java | 7 +- .../properties/SystemStorageProperties.java | 5 + .../properties/T1hDownloadProperties.java | 42 +++ .../controller/WeatherDataController.java | 73 ++-- .../java/org/jeecg/job/DownloadT1hJob.java | 314 ++++++++++++++++++ .../org/jeecg/service/WeatherDataService.java | 4 +- .../service/impl/WeatherDataServiceImpl.java | 57 ++-- .../org/jeecg/utils/ProcessOutputHandler.java | 39 +++ .../main/java/org/jeecg/utils/T1hUtils.java | 86 +++++ 13 files changed, 619 insertions(+), 69 deletions(-) create mode 100644 jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/T1hFilePrefixEnum.java create mode 100644 jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/T1hDownloadProperties.java create mode 100644 jeecg-module-weather/src/main/java/org/jeecg/job/DownloadT1hJob.java create mode 100644 jeecg-module-weather/src/main/java/org/jeecg/utils/ProcessOutputHandler.java create mode 100644 jeecg-module-weather/src/main/java/org/jeecg/utils/T1hUtils.java diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/WeatherPrefixConstants.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/WeatherPrefixConstants.java index a59732b..f2100a7 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/WeatherPrefixConstants.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/WeatherPrefixConstants.java @@ -5,4 +5,5 @@ public class WeatherPrefixConstants { public static final String PANGU_PREFIX = "panguweather_"; public static final String CRA40_PREFIX = "CRA40_"; public static final String NCEP_PREFIX = "cdas1"; + public static final String T1H_PREFIX = "T1H_"; } diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/T1hFilePrefixEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/T1hFilePrefixEnum.java new file mode 100644 index 0000000..ad1bb78 --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/T1hFilePrefixEnum.java @@ -0,0 +1,51 @@ +package org.jeecg.common.constant.enums; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 文件类型说明枚举 + */ +public enum T1hFilePrefixEnum { + + + T2MZ(0, "t2mz"), //2米气温 + PSZ(1, "psz"), //地表气压 + RH2M(2, "rh2m"), //2米相对湿度 + VGRD10M(3, "VGRD10m"), //10米经向风 + UGRD10M(4, "UGRD10m"); //10米纬向风 + + private Integer key; + + private String value; + + T1hFilePrefixEnum(Integer key, String value) { + this.key = key; + this.value = value; + } + + public Integer getKey(){ + return this.key; + } + + public String getValue(){ + return this.value; + } + + public static String getValueByKey(int key) { + for (T1hFilePrefixEnum prefix : T1hFilePrefixEnum.values()) { + if (prefix.getKey() == key) { + return prefix.getValue(); + } + } + return null; + } + + public static List getAllValues() { + return Arrays.stream(values()) + .map(T1hFilePrefixEnum::getValue) + .collect(Collectors.toList()); + } + +} diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/WeatherDataSourceEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/WeatherDataSourceEnum.java index 4ecd2ba..638a0c9 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/WeatherDataSourceEnum.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/WeatherDataSourceEnum.java @@ -21,7 +21,12 @@ public enum WeatherDataSourceEnum { /** * NCEP */ - NCEP(4,"NCEP"); + NCEP(4,"NCEP"), + + /** + * T1H + */ + T1H(5,"T1H"); private Integer key; diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/WeatherFileSuffixEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/WeatherFileSuffixEnum.java index db35dc1..3f0af7a 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/WeatherFileSuffixEnum.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/WeatherFileSuffixEnum.java @@ -6,7 +6,7 @@ package org.jeecg.common.constant.enums; public enum WeatherFileSuffixEnum { - GRIB("grib"),GRIB2("grib2"),GRB2("grb2");; + GRIB("grib"),GRIB2("grib2"),GRB2("grb2"),NC("nc"); private String value; diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/WeatherVariableNameEnum.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/WeatherVariableNameEnum.java index b29c6c0..10aea37 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/WeatherVariableNameEnum.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/constant/enums/WeatherVariableNameEnum.java @@ -20,7 +20,12 @@ public enum WeatherVariableNameEnum { NCEP_P(4, 1, "Pressure_msl"), NCEP_H(4, 2, "Relative_humidity_height_above_ground"), NCEP_U(4, 3, "u-component_of_wind_height_above_ground"), - NCEP_V(4, 4, "v-component_of_wind_height_above_ground"); + NCEP_V(4, 4, "v-component_of_wind_height_above_ground"), + T1H_T(5, 0, "t2mz"), + T1H_P(5, 1, "psz"), + T1H_H(5, 2, "rh2m"), + T1H_U(5, 3, "UGRD10m"), + T1H_V(5, 4, "VGRD10m"); private Integer type; diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/SystemStorageProperties.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/SystemStorageProperties.java index c5cf831..fc58cc0 100644 --- a/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/SystemStorageProperties.java +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/SystemStorageProperties.java @@ -29,6 +29,11 @@ public class SystemStorageProperties { */ private String ncep; + /** + * T1H数据存储路径 + */ + private String t1h; + /** * graphcast模型预测数据存储路径 */ diff --git a/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/T1hDownloadProperties.java b/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/T1hDownloadProperties.java new file mode 100644 index 0000000..874a07b --- /dev/null +++ b/jeecg-boot-base-core/src/main/java/org/jeecg/common/properties/T1hDownloadProperties.java @@ -0,0 +1,42 @@ +package org.jeecg.common.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "t1h-download") +public class T1hDownloadProperties { + + /** + * 下载T1H的Py脚本路径 + */ + private String downloadT1hPy; + + /** + * 合并T1H的Py脚本路径 + */ + private String mergeT1hPy; + + /** + * 未合并T1H的文件目录 + */ + private String t1hPath; + + /** + * T1H下载Key + */ + private String t1hKey; + + /** + * TJ1HCN(中国区域) 或 TJ1GB(全球区域) + */ + private String dataType; + + /** + * 下载小时数 + */ + private String hour; + +} diff --git a/jeecg-module-weather/src/main/java/org/jeecg/controller/WeatherDataController.java b/jeecg-module-weather/src/main/java/org/jeecg/controller/WeatherDataController.java index e70f3c9..eefadc0 100644 --- a/jeecg-module-weather/src/main/java/org/jeecg/controller/WeatherDataController.java +++ b/jeecg-module-weather/src/main/java/org/jeecg/controller/WeatherDataController.java @@ -1,14 +1,22 @@ package org.jeecg.controller; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import io.swagger.v3.oas.annotations.Operation; +import jakarta.annotation.Resource; import jakarta.validation.constraints.NotBlank; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.digest.DigestUtils; import org.jeecg.common.api.vo.Result; import org.jeecg.common.aspect.annotation.AutoLog; +import org.jeecg.common.constant.enums.T1hFilePrefixEnum; +import org.jeecg.common.constant.enums.WeatherDataSourceEnum; import org.jeecg.common.constant.enums.WeatherFileSuffixEnum; +import org.jeecg.common.properties.SystemStorageProperties; +import org.jeecg.common.properties.T1hDownloadProperties; import org.jeecg.common.system.query.PageRequest; +import org.jeecg.job.DownloadT1hJob; import org.jeecg.modules.base.entity.WeatherData; import org.jeecg.service.WeatherDataService; import org.jeecg.vo.FileExistVo; @@ -16,21 +24,31 @@ import org.jeecg.vo.FileUploadResultVo; import org.jeecg.vo.FileVo; import org.jeecg.vo.WeatherResultVO; import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.util.StopWatch; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import ucar.ma2.Array; import ucar.nc2.NetcdfFile; import ucar.nc2.Variable; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.time.LocalDate; import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.time.Year; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; +import java.util.stream.Stream; +@Slf4j @Validated @RestController @RequestMapping("weatherData") @@ -39,6 +57,9 @@ public class WeatherDataController { private final WeatherDataService weatherDataService; + @Resource + private DownloadT1hJob downloadT1hJob; + @AutoLog(value = "分页查询气象文件数据") @Operation(summary = "分页查询气象文件数据") @@ -117,42 +138,8 @@ public class WeatherDataController { return Result.OK(); } - public static String calculateMD5(String filePath) throws IOException { - try (InputStream is = new FileInputStream(filePath)) { - return DigestUtils.md5Hex(is); - } - } - - public static void main(String[] args) { - //reftime_ISO - String filePath = "E:\\runtimeEnv\\fileSystem\\weather\\pangu1\\panguweather_2025073118.grib"; - try { - String md5 = calculateMD5(filePath); - System.out.println("MD5: " + md5); - } catch (IOException e) { - e.printStackTrace(); - } -// try (NetcdfFile ncFile = NetcdfFile.open(filePath2)) { -// Variable variable = ncFile.findVariable("reftime_ISO"); -// if (variable != null) { -// Array data = variable.read(); -// System.out.println(variable.getFullName()); -// System.out.println(data.getObject(0)); -// } -// int index = 0; -// for (Variable variable : ncFile.getVariables()) { -// if (variable != null) { -// Array data = variable.read(); -// System.out.println(variable.getFullName()); -// System.out.println(data); -// if (index == 7) { -// break; -// } -// index++; -// } -// } -// }catch (Exception e){ -// -// } + @GetMapping("downloadT1HFile") + public void downloadT1HFile() { + downloadT1hJob.downloadT1HFile(); } } diff --git a/jeecg-module-weather/src/main/java/org/jeecg/job/DownloadT1hJob.java b/jeecg-module-weather/src/main/java/org/jeecg/job/DownloadT1hJob.java new file mode 100644 index 0000000..175b8ad --- /dev/null +++ b/jeecg-module-weather/src/main/java/org/jeecg/job/DownloadT1hJob.java @@ -0,0 +1,314 @@ +package org.jeecg.job; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; +import org.jeecg.common.constant.enums.T1hFilePrefixEnum; +import org.jeecg.common.constant.enums.WeatherDataSourceEnum; +import org.jeecg.common.properties.SystemStorageProperties; +import org.jeecg.common.properties.T1hDownloadProperties; +import org.jeecg.modules.base.entity.WeatherData; +import org.jeecg.service.WeatherDataService; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.util.StopWatch; +import org.springframework.web.bind.annotation.RestController; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Slf4j +@RestController +@RequiredArgsConstructor +public class DownloadT1hJob { + private final WeatherDataService weatherDataService; + private final SystemStorageProperties systemStorageProperties; + private final T1hDownloadProperties t1hDownloadProperties; + + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); + private static final int PROCESS_TIMEOUT_SECONDS = 3600; // 30分钟超时 + + @Scheduled(cron = "0 0 1 * * ?") + public void downloadT1HFile() { + log.info("开始执行T1H文件下载任务"); + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + try { + String baseTime = getBaseTime(); + // 第一阶段:下载文件 +// downloadAllT1hFiles(baseTime); + // 第二阶段:合并文件 +// mergeT1hFiles(baseTime); + // 合并后删除原始文件 + Arrays.stream(new File(getFullPath(t1hDownloadProperties.getT1hPath())).listFiles()).filter(File::isFile).forEach(File::delete); + // 更新气象文件信息 + saveWeatherData(); + log.info("T1H文件下载任务执行完成"); + } catch (Exception e) { + log.error("T1H文件下载任务执行失败", e); + throw new RuntimeException("T1H文件下载任务失败", e); + } finally { + stopWatch.stop(); + log.info("T1H文件下载任务执行耗时: {} 毫秒", stopWatch.getTotalTimeMillis()); + } + } + + /** + * 下载所有T1H文件 + */ + private void downloadAllT1hFiles(String baseTime) { + for (T1hFilePrefixEnum prefixEnum : T1hFilePrefixEnum.values()) { + String element = prefixEnum.getValue(); + log.info("开始处理元素: {}", element); + String[] FORECAST_HOURS_CONFIGS = {"(1, 1, 1)", "(6, 6, " + t1hDownloadProperties.getHour() + ")"}; + for (String forecastHours : FORECAST_HOURS_CONFIGS) { + executePythonScript(element, baseTime, forecastHours); + } + } + } + + /** + * 合并T1H文件 + */ + private void mergeT1hFiles(String baseTime) { + List variables = T1hFilePrefixEnum.getAllValues(); + List forecastTimes = generateForecastTimes(); + + ProcessBuilder processBuilder = new ProcessBuilder( + "python", + getPythonScriptPath(t1hDownloadProperties.getMergeT1hPy()), + "--indir", getFullPath(t1hDownloadProperties.getT1hPath()), + "--output_dir", getFullPath(systemStorageProperties.getT1h()), + "--variables", String.join(",", variables), + "--forecast_times", String.join(",", forecastTimes), + "--base_date", baseTime + ); + + executePythonProcess(processBuilder, "文件合并"); + } + + private void saveWeatherData(){ + //删除一个月前的文件 + LocalDateTime oneMonthAgo = LocalDateTime.now().minusMonths(1); + List weatherDatas = weatherDataService.list(new LambdaQueryWrapper().lt(WeatherData::getDataStartTime, oneMonthAgo)); + // 遍历删除文件 + weatherDatas.forEach(data -> { + if (data.getFilePath() != null && !data.getFilePath().isEmpty()) { + File file = new File(data.getFilePath()); + if (file.exists()) { + file.delete(); + } + } + }); + // 删除数据库记录 + weatherDataService.remove(new LambdaQueryWrapper().eq(WeatherData::getDataSource, WeatherDataSourceEnum.T1H.getKey())); + // 读取目录文件信息 + List weatherFileInfos = readFolderFiles(getFullPath(systemStorageProperties.getT1h())); + //保存文件信息 + weatherDataService.saveBatch(weatherFileInfos); + } + + /** + * 执行Python脚本 + */ + private void executePythonScript(String element, String baseTime, String forecastHours) { + String[] command = buildPythonCommand(element, baseTime, forecastHours); + log.info("执行Python脚本: {}", Arrays.toString(command)); + + ProcessBuilder processBuilder = new ProcessBuilder(command); + executePythonProcess(processBuilder, + String.format("元素%s预报时长%s下载", element, forecastHours)); + } + + /** + * 构建Python命令 + */ + private String[] buildPythonCommand(String element, String baseTime, String forecastHours) { + return new String[]{ + "python", + getPythonScriptPath(t1hDownloadProperties.getDownloadT1hPy()), + "--base-time", baseTime, + "--element", element, + "--output-dir", getFullPath(t1hDownloadProperties.getT1hPath()), + "--forecast-hours", forecastHours, + "--auth-token", t1hDownloadProperties.getT1hKey(), + "--data-type", t1hDownloadProperties.getDataType() + }; + } + + /** + * 执行Python进程 + */ + private void executePythonProcess(ProcessBuilder processBuilder, String processDescription) { + StopWatch processWatch = new StopWatch(); + processWatch.start(); + + Process process = null; + try { + process = processBuilder.start(); + + // 异步处理输出流 + CompletableFuture outputFuture = readStreamAsync(process.getInputStream(), "OUTPUT"); + CompletableFuture errorFuture = readStreamAsync(process.getErrorStream(), "ERROR"); + + // 等待进程完成(带超时) + boolean finished = process.waitFor(PROCESS_TIMEOUT_SECONDS, TimeUnit.SECONDS); + if (!finished) { + process.destroyForcibly(); + throw new RuntimeException(processDescription + "脚本执行超时"); + } + + // 等待输出处理完成 + CompletableFuture.allOf(outputFuture, errorFuture) + .get(10, TimeUnit.SECONDS); + + int exitCode = process.exitValue(); + processWatch.stop(); + + if (exitCode == 0) { + log.info("{}脚本执行成功,耗时: {} 毫秒", + processDescription, processWatch.getTotalTimeMillis()); + } else { + throw new RuntimeException(processDescription + + "脚本执行失败,退出码: " + exitCode); + } + + } catch (TimeoutException e) { + throw new RuntimeException(processDescription + "脚本执行超时", e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(processDescription + "脚本执行被中断", e); + } catch (Exception e) { + throw new RuntimeException(processDescription + "脚本执行失败", e); + } finally { + if (process != null) { + process.destroy(); + } + } + } + + /** + * 异步读取流 + */ + private CompletableFuture readStreamAsync(InputStream inputStream, String type) { + return CompletableFuture.runAsync(() -> { + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + + String line; + while ((line = reader.readLine()) != null) { + if ("ERROR".equals(type)) { + log.error("[Python {}] {}", type, line); + } else { + log.info("[Python {}] {}", type, line); + } + } + } catch (IOException e) { + log.error("处理Python{}流失败", type, e); + } + }); + } + + /** + * 生成预报时间列表 + */ + private List generateForecastTimes() { + List forecastTimes = new ArrayList<>(); + int hour = Integer.parseInt(t1hDownloadProperties.getHour()); + + for (int i = 0; i <= hour; i += 6) { + if (i == 0) { + forecastTimes.add(String.format("f%03d", i + 1)); + } else { + forecastTimes.add(String.format("f%03d", i)); + } + } + return forecastTimes; + } + + public List readFolderFiles(String folderPath) { + try (Stream paths = Files.list(Paths.get(folderPath))) { + return paths.filter(Files::isRegularFile) + .map(Path::toFile) + .map(this::extractFileInfo) + .collect(Collectors.toList()); + } catch (IOException e) { + throw new RuntimeException("读取文件夹失败: " + folderPath, e); + } + } + + private WeatherData extractFileInfo(File file) { + WeatherData data = new WeatherData(); + data.setFileName(file.getName()); + data.setFileSize((double) file.length() / 1024 / 1024); // MB + data.setFileExt(getFileExtension(file.getName())); + data.setFilePath(file.getAbsolutePath()); + data.setDataSource(WeatherDataSourceEnum.T1H.getKey()); + data.setDataStartTime(parseStartTimeFromFileName(file.getName())); + data.setCreateTime(new Date()); + data.setMd5Value(calculateMD5(file.getAbsolutePath())); + data.setShareTotal(1); + return data; + } + + private LocalDateTime parseStartTimeFromFileName(String fileName) { + // 从文件名解析时间 示例:"T1H_20251029_00.nc" 这样的格式 + try { + String dateStr = fileName.substring(4, 12); // 20251029 + String hourStr = fileName.substring(13, 15); // 00 + // 解析为 LocalDateTime(时分秒设置为 00:00:00) + return LocalDateTime.parse(dateStr + hourStr + "0000",DateTimeFormatter.ofPattern("yyyyMMddHHmmss")); + } catch (Exception e) { + e.printStackTrace(); + } + return null; // 解析失败返回当前时间 + } + + public String calculateMD5(String filePath){ + try (InputStream is = new FileInputStream(filePath)) { + return DigestUtils.md5Hex(is); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private String getFileExtension(String fileName) { + int lastDot = fileName.lastIndexOf("."); + return lastDot > 0 ? fileName.substring(lastDot + 1).toLowerCase() : ""; + } + + /** + * 获取完整路径 + */ + private String getFullPath(String relativePath) { + return systemStorageProperties.getRootPath() + File.separator + relativePath; + } + + /** + * 获取Python脚本路径 + */ + private String getPythonScriptPath(String scriptName) { + return systemStorageProperties.getRootPath() + File.separator + scriptName; + } + + /** + * 获取基准时间 + */ + private String getBaseTime() { + return LocalDateTime.now().format(DATE_FORMATTER) + "00"; + } +} \ No newline at end of file diff --git a/jeecg-module-weather/src/main/java/org/jeecg/service/WeatherDataService.java b/jeecg-module-weather/src/main/java/org/jeecg/service/WeatherDataService.java index f4a55bb..45d07fc 100644 --- a/jeecg-module-weather/src/main/java/org/jeecg/service/WeatherDataService.java +++ b/jeecg-module-weather/src/main/java/org/jeecg/service/WeatherDataService.java @@ -1,7 +1,9 @@ package org.jeecg.service; import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; import org.jeecg.common.system.query.PageRequest; +import org.jeecg.modules.base.entity.StasDataSource; import org.jeecg.modules.base.entity.WeatherData; import org.jeecg.vo.FileExistVo; import org.jeecg.vo.FileUploadResultVo; @@ -13,7 +15,7 @@ import java.time.LocalDateTime; import java.util.List; import java.util.Map; -public interface WeatherDataService { +public interface WeatherDataService extends IService { WeatherResultVO getWeatherData(Integer dataType, Integer weatherType, LocalDateTime startTime, int hour); WeatherResultVO getWeatherDataPreview(String weatherId, Integer weatherType); diff --git a/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherDataServiceImpl.java b/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherDataServiceImpl.java index 36e2f54..467c96f 100644 --- a/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherDataServiceImpl.java +++ b/jeecg-module-weather/src/main/java/org/jeecg/service/impl/WeatherDataServiceImpl.java @@ -81,6 +81,8 @@ public class WeatherDataServiceImpl extends ServiceImpl variables = getVariableNames(dataType); - if(WeatherDataSourceEnum.PANGU.getKey() == dataType || WeatherDataSourceEnum.NCEP.getKey() == dataType){ + if(WeatherDataSourceEnum.CRA40.getKey() != dataType){ try (NetcdfFile ncFile = NetcdfFile.open(filePath)) { // 读取数据(使用通用NcUtil方法) List> tData = getVariableData(ncFile, variables.get("temperature")); @@ -564,6 +563,12 @@ public class WeatherDataServiceImpl extends ServiceImpl> groupedFiles = groupAndSortFilesByDate(folder, targetDate); + printGroupedFiles(groupedFiles, targetDate); + } + + public static Map> groupAndSortFilesByDate(File folder, LocalDate targetDate) { + File[] files = folder.listFiles(); + if (files == null) return new HashMap<>(); + + String datePattern = targetDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")); + + return Arrays.stream(files) + .filter(File::isFile) + .map(File::getName) + .filter(name -> name.matches("\\d{10}_[a-zA-Z0-9]+_f\\d{3}\\.nc")) + .filter(name -> isFileForTargetDate(name, datePattern)) + .collect(Collectors.groupingBy( + fileName -> fileName.split("_")[1], + Collectors.collectingAndThen( + Collectors.toList(), + list -> list.stream() + .sorted(Comparator.comparingInt(fileName -> extractForecastTime(fileName))) // 使用lambda表达式 + .collect(Collectors.toList()) + ) + )); + } + + private static boolean isFileForTargetDate(String fileName, String targetDate) { + try { + String fileDate = fileName.substring(0, 8); + return fileDate.equals(targetDate); + } catch (Exception e) { + return false; + } + } + + private static int extractForecastTime(String fileName) { + try { + String timeStr = fileName.split("_")[2].replace("f", "").replace(".nc", ""); + return Integer.parseInt(timeStr); + } catch (Exception e) { + return 999; // 如果解析失败,放到最后 + } + } + + private static void printGroupedFiles(Map> groupedFiles, LocalDate targetDate) { + System.out.println("=== " + targetDate.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")) + " 的文件 ==="); + + if (groupedFiles.isEmpty()) { + System.out.println("未找到符合日期条件的文件"); + return; + } + + groupedFiles.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .forEach(entry -> { + System.out.println("变量: " + entry.getKey()); + entry.getValue().forEach(file -> System.out.println(" " + file)); + System.out.println(); + }); + + // 统计信息 + int totalFiles = groupedFiles.values().stream().mapToInt(List::size).sum(); + System.out.println("总计: " + groupedFiles.size() + " 个变量组, " + totalFiles + " 个文件"); + } +} \ No newline at end of file