1.完成天气预报计算任务执行接口
2.修改系统全局form-data请求编码格式 3.完成输运模拟贡献分析功能
This commit is contained in:
parent
259604d42a
commit
27e24880b8
|
|
@ -270,7 +270,7 @@ public interface CommonConstant {
|
|||
/**
|
||||
* 报表允许设计开发的角色
|
||||
*/
|
||||
public static String[] allowDevRoles = new String[]{"lowdeveloper", "admin"};
|
||||
String[] allowDevRoles = new String[]{"lowdeveloper", "admin"};
|
||||
|
||||
/**
|
||||
* 缓存用户最后一次收到消息通知的时间 KEY
|
||||
|
|
@ -286,4 +286,14 @@ public interface CommonConstant {
|
|||
* 缓存所有核设施key
|
||||
*/
|
||||
String ALL_NUCLEARFACILITY = "nuclearfacility";
|
||||
|
||||
/**
|
||||
* 输运模拟贡献分析数据KEY
|
||||
*/
|
||||
String TRANSPORT_CONTRIBUTION_ANALYSIS = "transport:contribution_analysis:";
|
||||
|
||||
/**
|
||||
* 输运模拟时序分析数据KEY
|
||||
*/
|
||||
String TRANSPORT_TIMING_ANALYSIS = "transport:timing_analysis:";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ public enum TransportTaskStatusEnum {
|
|||
/**
|
||||
* 执行失败
|
||||
*/
|
||||
ERROR(-1),
|
||||
FAILURE(-1),
|
||||
/**
|
||||
* 未开始
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package org.jeecg.common.constant.enums;
|
||||
|
||||
public enum TransportTimingAnalysisEnum {
|
||||
|
||||
/**
|
||||
* 3小时
|
||||
*/
|
||||
THREE_HOURS(3),
|
||||
/**
|
||||
* 6小时
|
||||
*/
|
||||
SIX_HOURS(-1),
|
||||
/**
|
||||
* 12小时
|
||||
*/
|
||||
TWELVE_HOURS(-1),
|
||||
/**
|
||||
* 24小时
|
||||
*/
|
||||
TWENTY_FOUR_HOURS(-1);
|
||||
|
||||
private Integer key;
|
||||
|
||||
TransportTimingAnalysisEnum(Integer key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public Integer getKey(){
|
||||
return this.key;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package org.jeecg.common.constant.enums;
|
||||
|
||||
/**
|
||||
* 盘古和Graphcast气象预测模型使用的数据源
|
||||
*/
|
||||
public enum WeatherForecastDatasourceEnum {
|
||||
|
||||
|
||||
CDS(1),
|
||||
LOCATION_FILE(2);
|
||||
|
||||
private Integer key;
|
||||
|
||||
WeatherForecastDatasourceEnum(Integer key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public Integer getKey(){
|
||||
return this.key;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
package org.jeecg.common.constant.enums;
|
||||
|
||||
/**
|
||||
* 预测模型说明枚举
|
||||
*/
|
||||
public enum WeatherModelEnum {
|
||||
|
||||
/**
|
||||
* 盘古模型
|
||||
*/
|
||||
PANGU(1),
|
||||
/**
|
||||
* Graphcast
|
||||
*/
|
||||
GRAPHCAST(2);
|
||||
|
||||
private Integer key;
|
||||
|
||||
WeatherModelEnum(Integer key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public Integer getKey(){
|
||||
return this.key;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,10 @@ package org.jeecg.common.constant.enums;
|
|||
*/
|
||||
public enum WeatherTaskStatusEnum {
|
||||
|
||||
/**
|
||||
* 执行失败
|
||||
*/
|
||||
FAILURE(-1),
|
||||
/**
|
||||
* 未开始
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -53,4 +53,19 @@ public class SystemStorageProperties {
|
|||
* CMAQ数据存储路径
|
||||
*/
|
||||
private String cmaqPath;
|
||||
|
||||
/**
|
||||
* ai-models 安装地址
|
||||
*/
|
||||
private String aiModelsPath;
|
||||
|
||||
/**
|
||||
* 盘古模型执行路径
|
||||
*/
|
||||
private String panguModelExecPath;
|
||||
|
||||
/**
|
||||
* graphcast模型执行路径
|
||||
*/
|
||||
private String graphcastModelExecPath;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import java.io.IOException;
|
|||
public class CopyTokenFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
request.setCharacterEncoding("UTF-8");
|
||||
// 以下为undertow定制代码,如切换其它servlet容器,需要同步更换
|
||||
HttpServletRequestImpl undertowRequest = (HttpServletRequestImpl) request;
|
||||
String token = request.getHeader("Authorization");
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public class WeatherData implements Serializable {
|
|||
private LocalDateTime dataStartTime;
|
||||
|
||||
/**
|
||||
* 数据来源(1-盘古模型,2-graphcast,3-cra40,4-ncep,5-t1h,6-fnl)
|
||||
* 数据来源(1-盘古模型,2-graphcast,3-cra40,4-ncep,5-fnl,6-t1h)
|
||||
*/
|
||||
@TableField(value = "data_source")
|
||||
private Integer dataSource;
|
||||
|
|
@ -62,12 +62,6 @@ public class WeatherData implements Serializable {
|
|||
@TableField(value = "file_path")
|
||||
private String filePath;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
@TableField(value = "create_by")
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,23 +1,37 @@
|
|||
package org.jeecg.modules.base.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Null;
|
||||
import lombok.Data;
|
||||
import org.jeecg.common.system.base.entity.BaseEntity;
|
||||
import org.jeecg.common.validgroup.InsertGroup;
|
||||
import org.jeecg.common.validgroup.UpdateGroup;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 天气预测任务表
|
||||
*/
|
||||
@Data
|
||||
@TableName("stas_weather_task")
|
||||
public class WeatherTask extends BaseEntity {
|
||||
public class WeatherTask{
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
@Null(message = "ID必须为空",groups = { InsertGroup.class})
|
||||
@NotNull(message = "ID不能为空",groups = { UpdateGroup.class})
|
||||
@TableId(type = IdType.INPUT)
|
||||
@Schema(description = "ID")
|
||||
private java.lang.String id;
|
||||
|
||||
/**
|
||||
* 任务名称
|
||||
|
|
@ -27,18 +41,18 @@ public class WeatherTask extends BaseEntity {
|
|||
private String taskName;
|
||||
|
||||
/**
|
||||
* 任务状态(0-未开始,1-运行中,2-已完成)
|
||||
* 任务状态(-1失败,0-未开始,1-运行中,2-已完成)
|
||||
*/
|
||||
@Null(message = "任务状态必须为空",groups = {InsertGroup.class, UpdateGroup.class})
|
||||
@TableField(value = "task_status")
|
||||
private Integer taskStatus;
|
||||
|
||||
/**
|
||||
* 耗时(小时),默认0
|
||||
* 耗时(分钟),默认0
|
||||
*/
|
||||
@Null(message = "耗时参数必须为空",groups = {InsertGroup.class, UpdateGroup.class})
|
||||
@TableField(value = "time_consuming")
|
||||
private Integer timeConsuming;
|
||||
private Double timeConsuming;
|
||||
|
||||
/**
|
||||
* 预测模型(1-盘古、2-Graphcast)
|
||||
|
|
@ -79,7 +93,38 @@ public class WeatherTask extends BaseEntity {
|
|||
/**
|
||||
* 如果data_sources为2,则存储本地文件路径
|
||||
*/
|
||||
@Null(message = "预测文件本地路径必须为空",groups = {InsertGroup.class, UpdateGroup.class})
|
||||
@TableField(value = "input_file")
|
||||
private String inputFile;
|
||||
|
||||
/**
|
||||
* 预测文件
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private MultipartFile file;
|
||||
|
||||
/**
|
||||
* 创建人
|
||||
*/
|
||||
private String createBy;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新人
|
||||
*/
|
||||
private String updateBy;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date updateTime;
|
||||
|
||||
}
|
||||
|
|
@ -7,9 +7,8 @@ import com.baomidou.mybatisplus.annotation.TableName;
|
|||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 天气预测任务日志表
|
||||
|
|
@ -41,6 +40,6 @@ public class WeatherTaskLog implements Serializable {
|
|||
* 创建时间
|
||||
*/
|
||||
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime createTime;
|
||||
private Date createTime;
|
||||
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ public class StationDataController {
|
|||
return Result.OK(stationDataService.getAllStations());
|
||||
}
|
||||
|
||||
@AutoLog(value = "查询所有台站")
|
||||
@AutoLog(value = "查询所有设施")
|
||||
@Operation(summary = "查询所有设施")
|
||||
@GetMapping("getAllNuclearfacility")
|
||||
public Result<?> getAllNuclearfacility(){
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package org.jeecg.controller;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
|
|
@ -43,6 +44,40 @@ public class TransportResultDataController {
|
|||
return Result.OK(transportResultDataService.getConformityAnalysis(taskId,stationId,nuclideName,facilityName));
|
||||
}
|
||||
|
||||
@AutoLog(value = "查询贡献分析数据")
|
||||
@Operation(summary = "查询贡献分析数据")
|
||||
@GetMapping("getContributionAnalysis")
|
||||
public Result<?> getContributionAnalysis(@NotNull(message = "任务id不能为空") Integer taskId,
|
||||
@NotBlank(message = "台站编码不能为空") String stationCode) {
|
||||
return Result.OK(transportResultDataService.getContributionAnalysis(taskId,stationCode));
|
||||
}
|
||||
|
||||
@AutoLog(value = "处理贡献分析数据-测试接口")
|
||||
@Operation(summary = "处理贡献分析数据-测试接口")
|
||||
@GetMapping("handleContributionAnalysis")
|
||||
public Result<?> handleContributionAnalysis(@NotNull(message = "任务id不能为空") Integer taskId) {
|
||||
transportResultDataService.handleContributionAnalysis(taskId);
|
||||
return Result.OK();
|
||||
}
|
||||
|
||||
@AutoLog(value = "查询反演核设施时序数据接口")
|
||||
@Operation(summary = "查询反演核设施时序数据接口")
|
||||
@GetMapping("getTimingAnalysis")
|
||||
public Result<?> getTimingAnalysis(@NotNull(message = "任务id不能为空") Integer taskId,
|
||||
@NotBlank(message = "台站编码不能为空") String stationCode,
|
||||
@NotBlank(message = "核设施名称不能为空") String facilityName,
|
||||
@NotNull(message = "时间序号不能为空") Integer timeNum) {
|
||||
return Result.OK(transportResultDataService.getTimingAnalysis(taskId,stationCode,facilityName,timeNum));
|
||||
}
|
||||
|
||||
@AutoLog(value = "处理反演核设施时序数据-测试接口")
|
||||
@Operation(summary = "处理反演核设施时序数据-测试接口")
|
||||
@GetMapping("handleTimingAnalysis")
|
||||
public Result<?> handleTimingAnalysis(@NotNull(message = "任务id不能为空") Integer taskId) {
|
||||
transportResultDataService.handleTimingAnalysis(taskId);
|
||||
return Result.OK();
|
||||
}
|
||||
|
||||
@AutoLog(value = "查询任务所属核设施数据")
|
||||
@Operation(summary = "查询任务所属核设施数据")
|
||||
@GetMapping("getTaskFacility")
|
||||
|
|
@ -50,4 +85,18 @@ public class TransportResultDataController {
|
|||
return Result.OK(transportResultDataService.getTaskFacility(taskId));
|
||||
}
|
||||
|
||||
@AutoLog(value = "查询任务所属台站数据")
|
||||
@Operation(summary = "查询任务所属台站数据")
|
||||
@GetMapping("getTaskStations")
|
||||
public Result<?> getTaskStations(@NotNull(message = "任务id不能为空") Integer taskId) {
|
||||
return Result.OK(transportResultDataService.getTaskStations(taskId));
|
||||
}
|
||||
|
||||
@AutoLog(value = "查询任务所属NC层级数据")
|
||||
@Operation(summary = "查询任务所属NC层级数据")
|
||||
@GetMapping("getTaskNCHeightLevel")
|
||||
public Result<?> getTaskNCHeightLevel(@NotNull(message = "任务id不能为空") Integer taskId) {
|
||||
return Result.OK(transportResultDataService.getTaskNCHeightLevel(taskId));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package org.jeecg.service;
|
||||
|
||||
import org.jeecg.vo.ContributionAnalysisVO;
|
||||
import org.jeecg.vo.QueryDiffusionVO;
|
||||
import org.jeecg.vo.TaskStationsVO;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -37,4 +39,49 @@ public interface TransportResultDataService {
|
|||
* @return
|
||||
*/
|
||||
List<String> getTaskFacility(Integer taskId);
|
||||
|
||||
/**
|
||||
* 获取贡献分析数据
|
||||
* @param taskId
|
||||
* @param stationCode
|
||||
* @return
|
||||
*/
|
||||
ContributionAnalysisVO getContributionAnalysis(Integer taskId, String stationCode);
|
||||
|
||||
/**
|
||||
* 处理贡献分析数据存入缓存
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
void handleContributionAnalysis(Integer taskId);
|
||||
|
||||
/**
|
||||
* 查询任务所属台站数据
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
List<TaskStationsVO> getTaskStations(Integer taskId);
|
||||
|
||||
/**
|
||||
* 查询任务所属NC层级数据
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
Map<Integer, Object> getTaskNCHeightLevel(Integer taskId);
|
||||
|
||||
/**
|
||||
* 查询反演核设施时序数据接口
|
||||
* @param taskId
|
||||
* @param stationCode
|
||||
* @param facilityName
|
||||
* @param timeNum
|
||||
*/
|
||||
Map<String,Double> getTimingAnalysis(Integer taskId,String stationCode,String facilityName,Integer timeNum);
|
||||
|
||||
/**
|
||||
* 处理反演核设施时序数据存入缓存
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
void handleTimingAnalysis(Integer taskId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ public interface TransportTaskService extends IService<TransportTask> {
|
|||
* @param taskId
|
||||
* @param minute
|
||||
*/
|
||||
void updateTaskTimeConsuming(Integer taskId, Double minute);
|
||||
void updateTaskStatusToCompleted(Integer taskId, Double minute);
|
||||
|
||||
/**
|
||||
* 删除任务日志
|
||||
|
|
|
|||
|
|
@ -2,22 +2,31 @@ package org.jeecg.service.impl;
|
|||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.enums.TransportTaskModeEnum;
|
||||
import org.jeecg.common.constant.enums.TransportTimingAnalysisEnum;
|
||||
import org.jeecg.common.properties.TransportSimulationProperties;
|
||||
import org.jeecg.common.util.NcUtil;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.entity.TransportTask;
|
||||
import org.jeecg.modules.base.entity.TransportTaskChild;
|
||||
import org.jeecg.modules.base.entity.configuration.GardsNuclearfacility;
|
||||
import org.jeecg.modules.base.entity.configuration.GardsStations;
|
||||
import org.jeecg.modules.base.mapper.TransportTaskChildMapper;
|
||||
import org.jeecg.modules.base.mapper.TransportTaskMapper;
|
||||
import org.jeecg.service.StationDataService;
|
||||
import org.jeecg.service.TransportResultDataService;
|
||||
import org.jeecg.util.BilinearInterpolatorWithMath;
|
||||
import org.jeecg.vo.ConcModValVo;
|
||||
import org.jeecg.vo.ContributionAnalysisVO;
|
||||
import org.jeecg.vo.QueryDiffusionVO;
|
||||
import org.jeecg.vo.TaskStationsVO;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ucar.ma2.Array;
|
||||
import ucar.ma2.DataType;
|
||||
|
|
@ -36,6 +45,7 @@ import java.time.ZoneId;
|
|||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TransportResultDataServiceImpl implements TransportResultDataService {
|
||||
|
|
@ -44,6 +54,7 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic
|
|||
private final TransportSimulationProperties simulationProperties;
|
||||
private final TransportTaskChildMapper transportTaskChildMapper;
|
||||
private final StationDataService stationDataService;
|
||||
private final RedisUtil redisUtil;
|
||||
private final static String FORWARD="forward";
|
||||
private final static String BACK_FORWARD="backward";
|
||||
|
||||
|
|
@ -236,7 +247,6 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic
|
|||
}
|
||||
|
||||
List<Map<String, Object>> xeResults = stationDataService.getXeResults(stationId, transportTask.getStartTime(), transportTask.getEndTime(), nuclideName);
|
||||
System.out.println(xeResults);
|
||||
//获取nc文件路径
|
||||
String path = this.getForwardTaskNCPath(transportTask);
|
||||
try (NetcdfFile ncFile = NetcdfFile.open(path.toString())) {
|
||||
|
|
@ -273,6 +283,7 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic
|
|||
List<ConcModValVo> modValList = new ArrayList<>();
|
||||
for(int i=pointNum;i<=maxPointNum;i++){
|
||||
for(int k=0;k<timeData.size();k++){
|
||||
//nageclass=1, pointspec=1, time=30, height=6, latitude=710, longitude=1430
|
||||
int[] origin = {0, i,k,0, latBestIndex,lonBestIndex};
|
||||
int[] section = {1, 1,1,1,1,1};
|
||||
Array levelData = spec001Mr.read(origin,section);
|
||||
|
|
@ -341,6 +352,326 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic
|
|||
return transportTaskChildren.stream().map(TransportTaskChild::getStationCode).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取贡献分析数据
|
||||
* @param taskId
|
||||
* @param stationCode
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public ContributionAnalysisVO getContributionAnalysis(Integer taskId,String stationCode) {
|
||||
TransportTask transportTask = transportTaskMapper.selectById(taskId);
|
||||
if(Objects.isNull(transportTask)){
|
||||
throw new RuntimeException("此任务不存在");
|
||||
}
|
||||
//包含从缓存中拿
|
||||
if(redisUtil.hasKey(CommonConstant.TRANSPORT_CONTRIBUTION_ANALYSIS+transportTask.getId()+":"+stationCode)){
|
||||
return (ContributionAnalysisVO) redisUtil.get(CommonConstant.TRANSPORT_CONTRIBUTION_ANALYSIS+transportTask.getId()+":"+stationCode);
|
||||
}
|
||||
throw new RuntimeException("此站点贡献分析数据不存在或未处理");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理贡献分析数据存入缓存
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public void handleContributionAnalysis(Integer taskId) {
|
||||
//查询任务数据
|
||||
TransportTask transportTask = transportTaskMapper.selectById(taskId);
|
||||
//查询需要处理贡献分析数据的台站
|
||||
LambdaQueryWrapper<TransportTaskChild> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(TransportTaskChild::getTaskId,taskId);
|
||||
List<TransportTaskChild> stationInfos = this.transportTaskChildMapper.selectList(queryWrapper);
|
||||
|
||||
//所以核设施数据
|
||||
List<GardsNuclearfacility> facilitys = stationDataService.getAllNuclearfacility();
|
||||
|
||||
NetcdfFile ncFile = null;
|
||||
try {
|
||||
for(TransportTaskChild stationInfo :stationInfos) {
|
||||
//每个台站的结果数据
|
||||
ContributionAnalysisVO contributionAnalysisVO = new ContributionAnalysisVO();
|
||||
//存储台站每天的浓度值数据
|
||||
Map<String,Double> stationEveryDayConcDatas = new LinkedHashMap<>();
|
||||
//存储核设施每天的浓度值数据
|
||||
Map<String,Map<String,Double>> facilityEveryDayConcDatas = new HashMap<>();
|
||||
//饼图数据
|
||||
Map<String,Double> pipeChartData = new HashMap<>();
|
||||
//总浓度值
|
||||
Double totalConc = 0D;
|
||||
//获取nc文件路径
|
||||
String path = this.getBackForwardTaskNCPath(transportTask,stationInfo.getStationCode());
|
||||
ncFile = NetcdfFile.open(path.toString());
|
||||
List<Double> lonData = NcUtil.getNCList(ncFile, "longitude");
|
||||
List<Double> latData = NcUtil.getNCList(ncFile, "latitude");
|
||||
List<Double> timeData = NcUtil.getNCList(ncFile, "time");
|
||||
Variable spec001Mr = ncFile.findVariable("spec001_mr");
|
||||
for(int k=0;k<timeData.size();k++){
|
||||
System.out.println(stationInfo.getStationCode()+"循环:"+k+",共"+timeData.size()+"次");
|
||||
//处理日期数据
|
||||
Instant instant = transportTask.getEndTime().toInstant();
|
||||
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
localDateTime = localDateTime.plusSeconds(timeData.get(k).intValue());
|
||||
String dayStr = LocalDateTimeUtil.format(localDateTime, "yyyy-MM-dd");
|
||||
if (!stationEveryDayConcDatas.containsKey(dayStr)) {
|
||||
stationEveryDayConcDatas.put(dayStr,0D);
|
||||
}
|
||||
//获取台站点位取整后±1.5度内的点坐标及数据,使用插值算法求,台站点位的值
|
||||
Double stationConc = this.getTargetSiteConc(lonData,latData,stationInfo.getLon(),stationInfo.getLat(),k,spec001Mr);
|
||||
//累加台站位置当前天的浓度数据
|
||||
stationEveryDayConcDatas.put(dayStr,stationEveryDayConcDatas.get(dayStr)+stationConc);
|
||||
|
||||
for (GardsNuclearfacility facility : facilitys){
|
||||
Double facilityConc = this.getTargetSiteConc(lonData,latData,facility.getLonValue(),facility.getLatValue(),k,spec001Mr);
|
||||
if (facilityConc>0){
|
||||
if (!facilityEveryDayConcDatas.containsKey(dayStr)) {
|
||||
Map<String,Double> facilityConcMap = new HashMap<>();
|
||||
facilityConcMap.put(facility.getFacilityName(),facilityConc);
|
||||
facilityEveryDayConcDatas.put(dayStr,facilityConcMap);
|
||||
}else {
|
||||
Map<String,Double> facilityConcMap = facilityEveryDayConcDatas.get(dayStr);
|
||||
if (!facilityConcMap.containsKey(facility.getFacilityName())) {
|
||||
facilityConcMap.put(facility.getFacilityName(),facilityConc);
|
||||
}else {
|
||||
facilityConcMap.put(facility.getFacilityName(),facilityConcMap.get(facility.getFacilityName())+facilityConc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//计算本模拟时间内总浓度数据
|
||||
totalConc = stationEveryDayConcDatas.values().stream().mapToDouble(Double::doubleValue).sum();
|
||||
//处理饼图数据
|
||||
if(CollUtil.isNotEmpty(facilityEveryDayConcDatas)){
|
||||
for (Map<String,Double> facilityConcMap : facilityEveryDayConcDatas.values()) {
|
||||
Set<Map.Entry<String, Double>> entries = facilityConcMap.entrySet();
|
||||
for (Map.Entry<String, Double> entry : entries) {
|
||||
if (!pipeChartData.containsKey(entry.getKey())) {
|
||||
pipeChartData.put(entry.getKey(),entry.getValue());
|
||||
}else {
|
||||
pipeChartData.put(entry.getKey(),pipeChartData.get(entry.getKey())+entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//处理返回值
|
||||
contributionAnalysisVO.setTotalConc(totalConc);
|
||||
contributionAnalysisVO.setStationEveryDayConcDatas(stationEveryDayConcDatas);
|
||||
contributionAnalysisVO.setFacilityEveryDayConcDatas(facilityEveryDayConcDatas);
|
||||
contributionAnalysisVO.setNuclideNames(new ArrayList<>(pipeChartData.keySet()));
|
||||
contributionAnalysisVO.setPipeChartData(pipeChartData);
|
||||
redisUtil.set(CommonConstant.TRANSPORT_CONTRIBUTION_ANALYSIS+transportTask.getId()+":"+stationInfo.getStationCode(), contributionAnalysisVO);
|
||||
ncFile.close();
|
||||
}
|
||||
}catch (IOException | InvalidRangeException e) {
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
try {
|
||||
if(ncFile !=null){
|
||||
ncFile.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询任务所属台站数据
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<TaskStationsVO> getTaskStations(Integer taskId) {
|
||||
TransportTask transportTask = this.transportTaskMapper.selectById(taskId);
|
||||
if(Objects.isNull(transportTask)){
|
||||
throw new RuntimeException("此任务不存在");
|
||||
}
|
||||
LambdaQueryWrapper<TransportTaskChild> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(TransportTaskChild::getTaskId,taskId);
|
||||
queryWrapper.select(TransportTaskChild::getId,TransportTaskChild::getStationCode);
|
||||
queryWrapper.orderByAsc(TransportTaskChild::getId);
|
||||
List<TransportTaskChild> transportTaskChildren = transportTaskChildMapper.selectList(queryWrapper);
|
||||
if(CollUtil.isEmpty(transportTaskChildren)){
|
||||
throw new RuntimeException("此任务站点信息不存在,请确认任务配置信息");
|
||||
}
|
||||
//本任务模拟的台站数据
|
||||
List<TaskStationsVO> taskStationsVOList = new ArrayList<>();
|
||||
for (int i = 0; i<transportTaskChildren.size();i++) {
|
||||
TaskStationsVO taskStationsVO = new TaskStationsVO();
|
||||
taskStationsVO.setStationNum(i+1);
|
||||
taskStationsVO.setStationCode(transportTaskChildren.get(i).getStationCode());
|
||||
taskStationsVOList.add(taskStationsVO);
|
||||
}
|
||||
return taskStationsVOList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询任务所属NC层级数据
|
||||
*
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Map<Integer, Object> getTaskNCHeightLevel(Integer taskId) {
|
||||
Map<Integer,Object> resultMap = new HashMap<>();
|
||||
|
||||
TransportTask transportTask = this.transportTaskMapper.selectById(taskId);
|
||||
if(Objects.isNull(transportTask)){
|
||||
throw new RuntimeException("此任务不存在");
|
||||
}
|
||||
LambdaQueryWrapper<TransportTaskChild> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(TransportTaskChild::getTaskId,taskId);
|
||||
queryWrapper.select(TransportTaskChild::getId,TransportTaskChild::getStationCode);
|
||||
queryWrapper.orderByAsc(TransportTaskChild::getId);
|
||||
List<TransportTaskChild> transportTaskChildren = transportTaskChildMapper.selectList(queryWrapper);
|
||||
if(CollUtil.isEmpty(transportTaskChildren)){
|
||||
throw new RuntimeException("此任务站点信息不存在,请确认任务配置信息");
|
||||
}
|
||||
//获取nc文件路径
|
||||
String path = "";
|
||||
if(TransportTaskModeEnum.FORWARD.getKey().equals(transportTask.getTaskMode())){
|
||||
path = this.getForwardTaskNCPath(transportTask);
|
||||
}else if(TransportTaskModeEnum.BACK_FORWARD.getKey().equals(transportTask.getTaskMode())){
|
||||
path = this.getBackForwardTaskNCPath(transportTask,transportTaskChildren.get(0).getStationCode());
|
||||
}
|
||||
if(!FileUtil.exist(path)){
|
||||
throw new RuntimeException("此任务模拟结果不存在,请确认任务运行状态");
|
||||
}
|
||||
//全局属性
|
||||
try (NetcdfFile ncFile = NetcdfFile.open(path.toString())) {
|
||||
Variable heightVar = ncFile.findVariable("height");
|
||||
Array data = heightVar.read();
|
||||
int[] shape = data.getShape();
|
||||
Index index = data.getIndex();
|
||||
for(int i=0;i<shape[0];i++){
|
||||
resultMap.put((i),data.getDouble(index.set(i)));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询反演核设施时序数据接口
|
||||
* @param taskId
|
||||
* @param stationCode
|
||||
* @param facilityName
|
||||
* @param timeNum
|
||||
*/
|
||||
@Override
|
||||
public Map<String,Double> getTimingAnalysis(Integer taskId,String stationCode,String facilityName,Integer timeNum) {
|
||||
TransportTask transportTask = transportTaskMapper.selectById(taskId);
|
||||
if(Objects.isNull(transportTask)){
|
||||
throw new RuntimeException("此任务不存在");
|
||||
}
|
||||
//包含从缓存中拿
|
||||
String key = CommonConstant.TRANSPORT_TIMING_ANALYSIS+transportTask.getId()+":"+stationCode+":"+facilityName;
|
||||
if(redisUtil.hasKey(key)){
|
||||
Map<String,Double> concValMap = (LinkedHashMap)redisUtil.get(key);
|
||||
//如果是3小时纬度,直接范围,因为最小是3小时
|
||||
if (TransportTimingAnalysisEnum.THREE_HOURS.getKey().equals(timeNum)) {
|
||||
concValMap.forEach((k,v)->{
|
||||
//纳克转换为毫克需除以1000000
|
||||
BigDecimal concValue = new BigDecimal(v);
|
||||
BigDecimal finalValue = concValue.divide(new BigDecimal(1000000)).setScale(5,BigDecimal.ROUND_HALF_UP);
|
||||
concValMap.put(k,finalValue.doubleValue());
|
||||
});
|
||||
return concValMap;
|
||||
}else{
|
||||
Map<String,Double> resultMap = new LinkedHashMap<>();
|
||||
Instant endTimeInstant = transportTask.getEndTime().toInstant();
|
||||
LocalDateTime endTime = LocalDateTime.ofInstant(endTimeInstant, ZoneId.systemDefault());
|
||||
|
||||
Instant startTimeInstant = transportTask.getStartTime().toInstant();
|
||||
LocalDateTime startTime = LocalDateTime.ofInstant(startTimeInstant, ZoneId.systemDefault());
|
||||
|
||||
boolean flag = true;
|
||||
while(flag){
|
||||
String dayTimeStr = LocalDateTimeUtil.format(startTime, "yyyy-MM-dd HH:mm:ss");
|
||||
//纳克转换为毫克需除以1000000
|
||||
BigDecimal concValue = new BigDecimal(concValMap.get(dayTimeStr));
|
||||
BigDecimal finalValue = concValue.divide(new BigDecimal(1000000)).setScale(5,BigDecimal.ROUND_HALF_UP);
|
||||
resultMap.put(dayTimeStr,finalValue.doubleValue());
|
||||
startTime = startTime.plusHours(timeNum);
|
||||
if(startTime.isEqual(endTime)){
|
||||
flag = false;
|
||||
}
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理反演核设施时序数据存入缓存
|
||||
* @param taskId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public void handleTimingAnalysis(Integer taskId) {
|
||||
TransportTask transportTask = this.transportTaskMapper.selectById(taskId);
|
||||
|
||||
//查询需要处理数据的台站
|
||||
LambdaQueryWrapper<TransportTaskChild> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(TransportTaskChild::getTaskId,taskId);
|
||||
List<TransportTaskChild> stationInfos = this.transportTaskChildMapper.selectList(queryWrapper);
|
||||
|
||||
//所以核设施数据
|
||||
List<GardsNuclearfacility> facilitys = stationDataService.getAllNuclearfacility();
|
||||
|
||||
NetcdfFile ncFile = null;
|
||||
try {
|
||||
for(TransportTaskChild stationInfo :stationInfos) {
|
||||
log.info("处理"+stationInfo.getStationCode()+"台站数据");
|
||||
Map<String,Map<String,Double>> everyFacilityConcDatas = new HashMap<>();
|
||||
//获取nc文件路径
|
||||
String path = this.getBackForwardTaskNCPath(transportTask,stationInfo.getStationCode());
|
||||
ncFile = NetcdfFile.open(path.toString());
|
||||
List<Double> lonData = NcUtil.getNCList(ncFile, "longitude");
|
||||
List<Double> latData = NcUtil.getNCList(ncFile, "latitude");
|
||||
List<Double> timeData = NcUtil.getNCList(ncFile, "time");
|
||||
Variable spec001Mr = ncFile.findVariable("spec001_mr");
|
||||
for (GardsNuclearfacility facility : facilitys){
|
||||
//存储台站每步的浓度值数据
|
||||
Map<String,Double> everyStepConcDatas = new LinkedHashMap<>();
|
||||
for(int k=0;k<timeData.size();k++){
|
||||
//处理日期数据
|
||||
Instant instant = transportTask.getEndTime().toInstant();
|
||||
LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
|
||||
localDateTime = localDateTime.plusSeconds(timeData.get(k).intValue());
|
||||
String dayTimeStr = LocalDateTimeUtil.format(localDateTime, "yyyy-MM-dd HH:mm:ss");
|
||||
//获取台站点位取整后±1.5度内的点坐标及数据,使用插值算法求,台站点位的值
|
||||
Double facilityConc = this.getTargetSiteConc(lonData,latData,facility.getLonValue(),facility.getLatValue(),k,spec001Mr);
|
||||
everyStepConcDatas.put(dayTimeStr,facilityConc);
|
||||
}
|
||||
everyFacilityConcDatas.put(facility.getFacilityName(),everyStepConcDatas);
|
||||
}
|
||||
if(CollUtil.isNotEmpty(everyFacilityConcDatas)){
|
||||
everyFacilityConcDatas.forEach((facilityName,facilityConcData)->{
|
||||
String key = CommonConstant.TRANSPORT_TIMING_ANALYSIS+transportTask.getId()+":"+stationInfo.getStationCode()+":"+facilityName;
|
||||
redisUtil.set(key, facilityConcData);
|
||||
});
|
||||
}
|
||||
ncFile.close();
|
||||
}
|
||||
}catch (IOException | InvalidRangeException e) {
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
try {
|
||||
if(ncFile !=null){
|
||||
ncFile.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 找到集合中里最接近目标值的值
|
||||
* @param data
|
||||
|
|
@ -406,4 +737,63 @@ public class TransportResultDataServiceImpl implements TransportResultDataServic
|
|||
path.append("grid_time_"+DateUtil.format(transportTask.getEndTime(),"yyyyMMddHHmmss")+".nc");
|
||||
return path.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在步长为 0.25° 的递增经纬度数组中,查找指定位置的索引
|
||||
* - lons[0] 是起始经度
|
||||
* - 步长严格为 0.25
|
||||
* - target 一定存在
|
||||
*/
|
||||
public int findTargetIndex(List<Double> datas, double target) {
|
||||
// 计算偏移量(单位:0.25°)
|
||||
double offset = (target - datas.get(0)) / 0.25;
|
||||
|
||||
// 四舍五入取整(抵抗浮点误差,如 166.5 - 166.0 = 0.5000000001 → 2.000...)
|
||||
int index = (int) Math.round(offset);
|
||||
|
||||
// 安全检查(可选)
|
||||
if (index < 0 || index >= datas.size() || Math.abs(datas.get(index) - target) > 1e-5) {
|
||||
System.out.println(datas);
|
||||
throw new IllegalArgumentException("经度 " + target + " 不在数组中");
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定位置的浓度数据
|
||||
* @param lonData nc文件的经度数据
|
||||
* @param latData nc文件的纬度数据
|
||||
* @param targetLon 目标位置的经度
|
||||
* @param targetLat 目标位置的纬度
|
||||
* @param k 时间索引
|
||||
* @param spec001Mr nc文件浓度数据变量
|
||||
* @return
|
||||
* @throws InvalidRangeException
|
||||
* @throws IOException
|
||||
*/
|
||||
private Double getTargetSiteConc(List<Double> lonData,List<Double> latData,Double targetLon,Double targetLat,int k,Variable spec001Mr) throws InvalidRangeException, IOException {
|
||||
int lonIndex = this.findTargetIndex(lonData, targetLon.intValue()-0.125);
|
||||
int latIndex = this.findTargetIndex(latData, targetLat.intValue()-0.125);
|
||||
//nageclass=1, pointspec=1, time=30, height=6, latitude=710, longitude=1430
|
||||
int[] origin = {0,0,k,0, (latIndex-6),(lonIndex-6)};
|
||||
int[] section = {1, 1,1,1,12,12};
|
||||
Array levelData = spec001Mr.read(origin,section);
|
||||
double[] pointData = (double[]) levelData.get1DJavaArray(DataType.DOUBLE);
|
||||
Map<BilinearInterpolatorWithMath.Point,Double> siteData = new LinkedHashMap<>();
|
||||
int nlon_patch = 12,patchLatStart = latIndex - 6,patchLonStart = lonIndex - 6;
|
||||
for(int i=0;i<pointData.length;i++){
|
||||
int iy_patch = i / nlon_patch; // 在子区域内的纬度偏移(0~11)
|
||||
int ix_patch = i % nlon_patch; // 在子区域内的经度偏移(0~11)
|
||||
// 转为全局索引(用于查 lonData/latData)
|
||||
int global_iy = patchLatStart + iy_patch;
|
||||
int global_ix = patchLonStart + ix_patch;
|
||||
// 获取真实经纬度
|
||||
double lon = lonData.get(global_ix);
|
||||
double lat = latData.get(global_iy);
|
||||
BilinearInterpolatorWithMath.Point point = new BilinearInterpolatorWithMath.Point(lon,lat);
|
||||
siteData.put(point,pointData[i]);
|
||||
}
|
||||
BilinearInterpolatorWithMath.GridData grid = new BilinearInterpolatorWithMath.GridData(siteData, 0.25, 0.125);
|
||||
return grid.interpolate(targetLon,targetLat);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.jeecg.common.constant.CommonConstant;
|
||||
import org.jeecg.common.constant.enums.TransportTaskStatusEnum;
|
||||
import org.jeecg.common.constant.enums.TransportTaskTypeEnum;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.properties.TransportSimulationProperties;
|
||||
import org.jeecg.common.system.query.PageRequest;
|
||||
import org.jeecg.common.util.RedisUtil;
|
||||
import org.jeecg.modules.base.entity.TransportTask;
|
||||
import org.jeecg.modules.base.entity.TransportTaskChild;
|
||||
import org.jeecg.modules.base.entity.TransportTaskLog;
|
||||
|
|
@ -40,6 +42,7 @@ public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,Tr
|
|||
private final WeatherDataMapper weatherDataMapper;
|
||||
private final TransportSimulationProperties simulationProperties;
|
||||
private final SystemStorageProperties systemStorageProperties;
|
||||
private final RedisUtil redisUtil;
|
||||
|
||||
/**
|
||||
* 分页查询任务列表
|
||||
|
|
@ -169,6 +172,9 @@ public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,Tr
|
|||
queryWrapper.eq(TransportTaskChild::getTaskId,id);
|
||||
transportTaskChildMapper.delete(queryWrapper);
|
||||
this.baseMapper.deleteById(id);
|
||||
//删除存储的贡献分析数据和时序分析数据
|
||||
redisUtil.del(CommonConstant.TRANSPORT_CONTRIBUTION_ANALYSIS+id);
|
||||
redisUtil.del(CommonConstant.TRANSPORT_TIMING_ANALYSIS+id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -231,10 +237,11 @@ public class TransportTaskServiceImpl extends ServiceImpl<TransportTaskMapper,Tr
|
|||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void updateTaskTimeConsuming(Integer taskId, Double minute) {
|
||||
public void updateTaskStatusToCompleted(Integer taskId, Double minute) {
|
||||
TransportTask transportTask = this.baseMapper.selectById(taskId);
|
||||
transportTask.setTaskStatus(TransportTaskStatusEnum.FAILURE.getValue());
|
||||
transportTask.setTimeConsuming(minute);
|
||||
|
||||
this.baseMapper.updateById(transportTask);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package org.jeecg.task;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
|
|
@ -74,14 +72,15 @@ public class TransportTaskExec extends Thread{
|
|||
}catch (Exception e){
|
||||
String taskErrorLog = "任务执行失败,原因:"+e.getMessage();
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.transportTask.getId(),taskErrorLog));
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}finally {
|
||||
//添加任务耗时
|
||||
stopWatch.stop();
|
||||
long seconds = stopWatch.getTime(TimeUnit.SECONDS);
|
||||
Double min = seconds/60D;
|
||||
this.transportTaskService.updateTaskTimeConsuming(this.transportTask.getId(),min);
|
||||
this.transportTaskService.updateTaskStatusToCompleted(this.transportTask.getId(),min);
|
||||
String taskCompletedLog = "任务执行完成,耗时:"+min+"分钟";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.transportTask.getId(),taskCompletedLog));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,175 @@
|
|||
package org.jeecg.util;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class BilinearInterpolatorWithMath {
|
||||
|
||||
public static class Point {
|
||||
public final double lon;
|
||||
public final double lat;
|
||||
|
||||
public Point(double lon, double lat) {
|
||||
this.lon = lon;
|
||||
this.lat = lat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Point p = (Point) o;
|
||||
// 使用 Math.abs 做浮点容差比较
|
||||
double tol = 1e-9;
|
||||
return Math.abs(p.lon - lon) < tol && Math.abs(p.lat - lat) < tol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// 使用 Math.floor + longBitsToDouble 生成稳定哈希(可选优化)
|
||||
return java.util.Objects.hash(
|
||||
Double.doubleToLongBits(Math.floor(lon * 1e6 + 0.5) / 1e6),
|
||||
Double.doubleToLongBits(Math.floor(lat * 1e6 + 0.5) / 1e6)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static class GridData {
|
||||
private final Map<Point, Double> data;
|
||||
private final double delta;
|
||||
private final double offset;
|
||||
|
||||
public GridData(Map<Point, Double> data, double delta, double offset) {
|
||||
this.data = data;
|
||||
this.delta = delta;
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算双线性插值
|
||||
*/
|
||||
public Double interpolate(double lon, double lat) {
|
||||
//精确匹配格点(避免边界问题)
|
||||
Point target = new Point(lon, lat);
|
||||
Double exactValue = data.get(target);
|
||||
if (exactValue != null && !Double.isNaN(exactValue)) {
|
||||
return exactValue; // 直接返回,不插值
|
||||
}
|
||||
|
||||
//1. 定位网格单元:使用 Math.floor 确保负数正确处理
|
||||
// 关键:避免 (int) 强转的 truncation 问题
|
||||
int i = (int) Math.floor((lon - offset) / delta);
|
||||
int j = (int) Math.floor((lat - offset) / delta);
|
||||
|
||||
// 计算四个角点坐标
|
||||
double lon0 = i * delta + offset;
|
||||
double lon1 = lon0 + delta;
|
||||
double lat0 = j * delta + offset;
|
||||
double lat1 = lat0 + delta;
|
||||
|
||||
//2. 防御性检查:目标点是否真的在 [lon0, lon1) × [lat0, lat1) 内?
|
||||
// 使用 Math.abs + 容差避免浮点误差导致误判
|
||||
final double TOL = 1e-10;
|
||||
if (lon < lon0 - TOL || lon > lon1 + TOL ||
|
||||
lat < lat0 - TOL || lat > lat1 + TOL) {
|
||||
System.err.printf("Warning: (%.6f, %.6f) is outside computed cell [%.3f~%.3f]×[%.3f~%.3f]%n",
|
||||
lon, lat, lon0, lon1, lat0, lat1);
|
||||
// 可选:自动修正索引
|
||||
i = (int) Math.floor((lon - offset) / delta); // 再算一次
|
||||
j = (int) Math.floor((lat - offset) / delta);
|
||||
lon0 = i * delta + offset;
|
||||
lon1 = lon0 + delta;
|
||||
lat0 = j * delta + offset;
|
||||
lat1 = lat0 + delta;
|
||||
}
|
||||
|
||||
//3. 构造四邻点并查值
|
||||
Point[] points = {
|
||||
new Point(lon0, lat0), // SW
|
||||
new Point(lon1, lat0), // SE
|
||||
new Point(lon0, lat1), // NW
|
||||
new Point(lon1, lat1) // NE
|
||||
};
|
||||
Double[] values = new Double[4];
|
||||
boolean hasMissing = false;
|
||||
for (int k = 0; k < 4; k++) {
|
||||
values[k] = data.get(points[k]);
|
||||
if (values[k] == null || Double.isNaN(values[k])) {
|
||||
hasMissing = true;
|
||||
System.err.println("Missing/NaN at: " + points[k].lon + ", " + points[k].lat);
|
||||
}
|
||||
}
|
||||
if (hasMissing) return null;
|
||||
|
||||
//4. 计算权重:使用 Math.max/Math.min 裁剪到 [0,1](防浮点误差溢出)
|
||||
double wx = (lon - lon0) / delta;
|
||||
double wy = (lat - lat0) / delta;
|
||||
|
||||
// 关键增强:防止 wx/wy 因浮点误差略超 [0,1]
|
||||
wx = Math.max(0.0, Math.min(1.0, wx)); // 等价于 clamp(wx, 0, 1)
|
||||
wy = Math.max(0.0, Math.min(1.0, wy));
|
||||
|
||||
//5. 双线性插值公式(核心,无需 Math 复杂函数)
|
||||
double v00 = values[0], v10 = values[1], v01 = values[2], v11 = values[3];
|
||||
double val = (1 - wx) * (1 - wy) * v00 +
|
||||
wx * (1 - wy) * v10 +
|
||||
(1 - wx) * wy * v01 +
|
||||
wx * wy * v11;
|
||||
|
||||
//6. 最终检查:结果是否合理?
|
||||
// 使用 Math.abs + isFinite 防 NaN/Inf
|
||||
if (!Double.isFinite(val)) {
|
||||
System.err.println("Interpolation produced invalid value: " + val);
|
||||
return null;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插值(高效处理多个点)
|
||||
* 展示 Math.copySign / Math.IEEEremainder 等高级用法(可选场景)
|
||||
*/
|
||||
public double[] interpolateBatch(double[] lons, double[] lats) {
|
||||
if (lons.length != lats.length)
|
||||
throw new IllegalArgumentException("Length mismatch");
|
||||
|
||||
double[] results = new double[lons.length];
|
||||
|
||||
for (int idx = 0; idx < lons.length; idx++) {
|
||||
Double val = interpolate(lons[idx], lats[idx]);
|
||||
results[idx] = (val != null) ? val : Double.NaN;
|
||||
|
||||
// 可选:对经度做环绕处理(全球数据常见)
|
||||
// 例如:将 -181° 映射到 179°
|
||||
double adjustedLon = lons[idx];
|
||||
if (Math.abs(adjustedLon) > 180.0) {
|
||||
// 使用 IEEE 754 标准取余(比 % 更可靠)
|
||||
adjustedLon = Math.IEEEremainder(adjustedLon + 180.0, 360.0) - 180.0;
|
||||
// 保持符号一致性(-0.0 → 0.0)
|
||||
adjustedLon = Math.copySign(adjustedLon, adjustedLon);
|
||||
}
|
||||
// 然后用 adjustedLon 重新插值...
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 演示:Math 库增强版用法 =====
|
||||
public static void main(String[] args) {
|
||||
Map<Point, Double> data = new LinkedHashMap<>();
|
||||
data.put(new Point(7.875, 47.875), 195.53140258789062);
|
||||
data.put(new Point(8.125, 47.875), 9.2166166305542);
|
||||
data.put(new Point(7.875, 48.125), 40.02995681762695);
|
||||
data.put(new Point(8.125, 48.125), 11.597820281982422);
|
||||
|
||||
GridData grid = new GridData(data, 0.25, 0.125);
|
||||
|
||||
// 测试正常点
|
||||
System.out.printf("Normal: %.8f%n",
|
||||
grid.interpolate(7.9, 47.9));
|
||||
|
||||
// 测试边界点(wx/wy 接近 1)
|
||||
// System.out.printf("Edge case: %.8f%n",
|
||||
// grid.interpolate(-53.625, -36.375)); // 应等于 NE 点值
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package org.jeecg.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 贡献分析数据VO
|
||||
*/
|
||||
@Data
|
||||
public class ContributionAnalysisVO {
|
||||
|
||||
/**
|
||||
* X轴台站每天的浓度值数据
|
||||
*/
|
||||
private Map<String,Double> stationEveryDayConcDatas;
|
||||
|
||||
/**
|
||||
* 核设施每天的浓度值数据
|
||||
*/
|
||||
private Map<String,Map<String,Double>> facilityEveryDayConcDatas;
|
||||
|
||||
/**
|
||||
* 涉及的核设施名称
|
||||
*/
|
||||
private List<String> nuclideNames;
|
||||
|
||||
/**
|
||||
* 饼图数据
|
||||
*/
|
||||
private Map<String,Double> pipeChartData;
|
||||
|
||||
/**
|
||||
* 总浓度值
|
||||
*/
|
||||
private Double totalConc;
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package org.jeecg.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TaskStationsVO {
|
||||
|
||||
private Integer stationId;
|
||||
|
||||
private Integer stationNum;
|
||||
|
||||
private String stationCode;
|
||||
|
||||
private Double lon;
|
||||
|
||||
private Double lat;
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ import org.jeecg.common.validgroup.InsertGroup;
|
|||
import org.jeecg.common.validgroup.UpdateGroup;
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
import org.jeecg.service.WeatherTaskService;
|
||||
import org.jeecg.vo.ForecastFileVO;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
|
@ -44,7 +45,8 @@ public class WeatherTaskController {
|
|||
@AutoLog(value = "新增天气预测任务")
|
||||
@Operation(summary = "新增天气预测任务")
|
||||
@PostMapping("create")
|
||||
public Result<?> create(@RequestBody @Validated(value = InsertGroup.class) WeatherTask weatherTask){
|
||||
public Result<?> create(@Validated(value = InsertGroup.class) WeatherTask weatherTask){
|
||||
System.out.println(weatherTask);
|
||||
weatherTaskService.cteate(weatherTask);
|
||||
return Result.OK();
|
||||
}
|
||||
|
|
@ -59,7 +61,7 @@ public class WeatherTaskController {
|
|||
@AutoLog(value = "修改天气预测任务")
|
||||
@Operation(summary = "修改天气预测任务")
|
||||
@PutMapping("update")
|
||||
public Result<?> update(@RequestBody @Validated(value = UpdateGroup.class) WeatherTask weatherTask){
|
||||
public Result<?> update(@Validated(value = UpdateGroup.class) WeatherTask weatherTask){
|
||||
weatherTaskService.update(weatherTask);
|
||||
return Result.OK();
|
||||
}
|
||||
|
|
@ -72,6 +74,14 @@ public class WeatherTaskController {
|
|||
return Result.OK();
|
||||
}
|
||||
|
||||
@AutoLog(value = "启动任务")
|
||||
@Operation(summary = "启动任务")
|
||||
@PutMapping("runTask")
|
||||
public Result<?> runTask(@NotBlank(message = "任务ID不能为空") String taskId){
|
||||
weatherTaskService.runTask(taskId);
|
||||
return Result.OK();
|
||||
}
|
||||
|
||||
@AutoLog(value = "获取天气预测任务过程日志")
|
||||
@Operation(summary = "获取天气预测任务过程日志")
|
||||
@GetMapping("getTaskLog")
|
||||
|
|
|
|||
|
|
@ -3,15 +3,11 @@ 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.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface WeatherDataService extends IService<WeatherData> {
|
||||
|
||||
|
|
@ -54,4 +50,10 @@ public interface WeatherDataService extends IService<WeatherData> {
|
|||
* 处理静态气象数据入库接口,比上传快
|
||||
*/
|
||||
void handleStaticDataToDB(String path,Integer dataSource);
|
||||
|
||||
/**
|
||||
* 保存模型预测好的文件信息到WeatherData
|
||||
* @param weatherData
|
||||
*/
|
||||
void saveFileInfo(WeatherData weatherData);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public interface WeatherTaskService extends IService<WeatherTask> {
|
|||
* 运行任务
|
||||
* @param id
|
||||
*/
|
||||
void runTask(Integer id);
|
||||
void runTask(String id);
|
||||
|
||||
/**
|
||||
* 获取任务运行日志
|
||||
|
|
@ -61,4 +61,31 @@ public interface WeatherTaskService extends IService<WeatherTask> {
|
|||
* @return
|
||||
*/
|
||||
List<WeatherTaskLog> getTaskLog(String taskId);
|
||||
|
||||
/**
|
||||
* 保存任务过程日志
|
||||
* @param log
|
||||
*/
|
||||
void saveLog(WeatherTaskLog log);
|
||||
|
||||
/**
|
||||
* 修改任务运行状态
|
||||
* @param taskId
|
||||
* @param taskStatus
|
||||
*/
|
||||
void updateTaskStatus(String taskId,Integer taskStatus);
|
||||
|
||||
/**
|
||||
* 根据任务id删除任务日志
|
||||
* @param taskId
|
||||
*/
|
||||
void deleteTaskLog(String taskId);
|
||||
|
||||
/**
|
||||
* 修改任务状态为结束
|
||||
* @param taskId
|
||||
* @param timeConsuming
|
||||
*/
|
||||
void updateTaskStatusToCompleted(String taskId, Double timeConsuming);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package org.jeecg.service.impl;
|
|||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.StringPool;
|
||||
|
|
@ -435,8 +436,7 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
BigDecimal fileSize = bg.divide(divideVal).divide(divideVal).setScale(2, RoundingMode.HALF_UP);
|
||||
queryResult.setFileSize(fileSize.doubleValue());
|
||||
//把文件移入新路径
|
||||
int year = utcDateTime.getYear();
|
||||
String newFileDirPath = dataFile.getParentFile().getParent()+File.separator+year+File.separator;
|
||||
String newFileDirPath = dataFile.getParentFile().getParent()+File.separator+File.separator;
|
||||
String newFilePath = newFileDirPath+File.separator+dataFile.getName();
|
||||
FileUtil.mkdir(newFileDirPath);
|
||||
File parentDir = dataFile.getParentFile();
|
||||
|
|
@ -557,6 +557,17 @@ public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, Weath
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存模型预测好的文件信息到WeatherData
|
||||
*
|
||||
* @param weatherData
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void saveFileInfo(WeatherData weatherData) {
|
||||
this.save(weatherData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件合并
|
||||
* @param fileVo
|
||||
|
|
|
|||
|
|
@ -2,20 +2,29 @@ package org.jeecg.service.impl;
|
|||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
import org.jeecg.common.constant.enums.WeatherFileSuffixEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherForecastDatasourceEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherTaskStatusEnum;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.system.query.PageRequest;
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
import org.jeecg.modules.base.entity.WeatherTaskLog;
|
||||
import org.jeecg.modules.base.mapper.WeatherDataMapper;
|
||||
import org.jeecg.modules.base.mapper.WeatherTaskLogMapper;
|
||||
import org.jeecg.modules.base.mapper.WeatherTaskMapper;
|
||||
import org.jeecg.service.WeatherTaskService;
|
||||
import org.jeecg.task.WeatherTaskExec;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
|
@ -29,6 +38,8 @@ import java.util.Objects;
|
|||
public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, WeatherTask> implements WeatherTaskService {
|
||||
|
||||
private final WeatherTaskLogMapper weatherTaskLogMapper;
|
||||
private final SystemStorageProperties systemStorageProperties;
|
||||
private final WeatherDataMapper weatherDataMapper;
|
||||
|
||||
/**
|
||||
* 分页查询任务列表
|
||||
|
|
@ -73,11 +84,40 @@ public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, Weath
|
|||
LambdaQueryWrapper<WeatherTask> checkDateWrapper = new LambdaQueryWrapper<>();
|
||||
checkDateWrapper.eq(WeatherTask::getStartDate,weatherTask.getStartDate());
|
||||
checkDateWrapper.eq(WeatherTask::getStartTime,weatherTask.getStartTime());
|
||||
checkDateWrapper.eq(WeatherTask::getPredictionModel,weatherTask.getPredictionModel());
|
||||
WeatherTask checkDateResult = this.getOne(checkDateWrapper);
|
||||
if(Objects.nonNull(checkDateResult)){
|
||||
throw new RuntimeException("此当前预测时间为参数的任务已存在");
|
||||
}
|
||||
//手动获取id
|
||||
String id = IdWorker.getIdStr();
|
||||
weatherTask.setId(id);
|
||||
weatherTask.setTaskStatus(WeatherTaskStatusEnum.NOT_STARTED.getKey());
|
||||
if (WeatherForecastDatasourceEnum.LOCATION_FILE.getKey().equals(weatherTask.getDataSources())){
|
||||
try {
|
||||
MultipartFile file = weatherTask.getFile();
|
||||
//构造文件名称
|
||||
StringBuilder fileName = new StringBuilder();
|
||||
fileName.append(file.getOriginalFilename().substring(0,file.getOriginalFilename().lastIndexOf(".")));
|
||||
fileName.append("_"+id+".");
|
||||
fileName.append(WeatherFileSuffixEnum.GRIB.getValue());
|
||||
|
||||
//文件保存路径
|
||||
StringBuilder filePath = new StringBuilder();
|
||||
filePath.append(this.systemStorageProperties.getPanguModelExecPath());
|
||||
filePath.append(File.separator);
|
||||
filePath.append(fileName);
|
||||
|
||||
File storageFile = new File(filePath.toString());
|
||||
if (storageFile.exists()){
|
||||
storageFile.delete();
|
||||
}
|
||||
file.transferTo(storageFile);
|
||||
weatherTask.setInputFile(fileName.toString());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
this.save(weatherTask);
|
||||
}
|
||||
|
||||
|
|
@ -115,8 +155,9 @@ public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, Weath
|
|||
LambdaQueryWrapper<WeatherTask> checkDateWrapper = new LambdaQueryWrapper<>();
|
||||
checkDateWrapper.eq(WeatherTask::getStartDate,weatherTask.getStartDate());
|
||||
checkDateWrapper.eq(WeatherTask::getStartTime,weatherTask.getStartTime());
|
||||
checkDateWrapper.eq(WeatherTask::getPredictionModel,weatherTask.getPredictionModel());
|
||||
WeatherTask checkDateResult = this.getOne(checkDateWrapper);
|
||||
if(Objects.nonNull(checkDateResult) && !weatherTask.getId().equals(checkNameResult.getId())){
|
||||
if(Objects.nonNull(checkDateResult) && Objects.nonNull(checkNameResult) && !weatherTask.getId().equals(checkNameResult.getId())){
|
||||
throw new RuntimeException("此当前预测时间为参数的任务已存在");
|
||||
}
|
||||
queryResult.setTaskName(weatherTask.getTaskName());
|
||||
|
|
@ -125,7 +166,35 @@ public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, Weath
|
|||
queryResult.setStartTime(weatherTask.getStartTime());
|
||||
queryResult.setLeadTime(weatherTask.getLeadTime());
|
||||
queryResult.setDataSources(weatherTask.getDataSources());
|
||||
queryResult.setInputFile(weatherTask.getInputFile());
|
||||
if (WeatherForecastDatasourceEnum.LOCATION_FILE.getKey().equals(weatherTask.getDataSources())){
|
||||
if (Objects.nonNull(weatherTask.getFile())){
|
||||
try {
|
||||
MultipartFile file = weatherTask.getFile();
|
||||
//构造文件名称
|
||||
StringBuilder fileName = new StringBuilder();
|
||||
fileName.append(file.getOriginalFilename().substring(0,file.getOriginalFilename().lastIndexOf(".")));
|
||||
fileName.append("_"+queryResult.getId()+".");
|
||||
fileName.append(WeatherFileSuffixEnum.GRIB.getValue());
|
||||
|
||||
//文件保存路径
|
||||
StringBuilder filePath = new StringBuilder();
|
||||
filePath.append(this.systemStorageProperties.getPanguModelExecPath());
|
||||
filePath.append(File.separator);
|
||||
filePath.append(fileName);
|
||||
|
||||
File storageFile = new File(filePath.toString());
|
||||
if (storageFile.exists()){
|
||||
storageFile.delete();
|
||||
}
|
||||
file.transferTo(storageFile);
|
||||
queryResult.setInputFile(fileName.toString());
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
queryResult.setInputFile(Strings.EMPTY);
|
||||
}
|
||||
this.updateById(queryResult);
|
||||
}
|
||||
|
||||
|
|
@ -145,8 +214,15 @@ public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, Weath
|
|||
* @param id
|
||||
*/
|
||||
@Override
|
||||
public void runTask(Integer id) {
|
||||
|
||||
public void runTask(String id) {
|
||||
WeatherTask task = this.baseMapper.selectById(id);
|
||||
if(Objects.isNull(task)){
|
||||
throw new RuntimeException("此任务不存在");
|
||||
}
|
||||
WeatherTaskExec exec = new WeatherTaskExec();
|
||||
exec.init(task,this,systemStorageProperties,weatherDataMapper);
|
||||
exec.setName("天气预测任务线程");
|
||||
exec.start();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -163,4 +239,57 @@ public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, Weath
|
|||
queryWrapper.orderByAsc(WeatherTaskLog::getCreateTime);
|
||||
return this.weatherTaskLogMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存任务过程日志
|
||||
*
|
||||
* @param log
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void saveLog(WeatherTaskLog log) {
|
||||
this.weatherTaskLogMapper.insert(log);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改任务运行状态
|
||||
*
|
||||
* @param taskId
|
||||
* @param taskStatus
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void updateTaskStatus(String taskId, Integer taskStatus) {
|
||||
WeatherTask weatherTask = this.baseMapper.selectById(taskId);
|
||||
weatherTask.setTaskStatus(taskStatus);
|
||||
this.updateById(weatherTask);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据任务id删除任务日志
|
||||
*
|
||||
* @param taskId
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void deleteTaskLog(String taskId) {
|
||||
LambdaQueryWrapper<WeatherTaskLog> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(WeatherTaskLog::getTaskId,taskId);
|
||||
this.weatherTaskLogMapper.delete(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改任务状态为结束
|
||||
*
|
||||
* @param taskId
|
||||
* @param timeConsuming
|
||||
*/
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
@Override
|
||||
public void updateTaskStatusToCompleted(String taskId, Double timeConsuming) {
|
||||
WeatherTask weatherTask = this.baseMapper.selectById(taskId);
|
||||
weatherTask.setTaskStatus(WeatherTaskStatusEnum.COMPLETED.getKey());
|
||||
weatherTask.setTimeConsuming(timeConsuming);
|
||||
this.updateById(weatherTask);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package org.jeecg.task;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 日志事件
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class ProgressEvent implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String taskId;
|
||||
|
||||
/**
|
||||
* 过程日志
|
||||
*/
|
||||
private String content;
|
||||
|
||||
public ProgressEvent(String taskId, String content) {
|
||||
this.taskId = taskId;
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package org.jeecg.task;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.modules.base.entity.WeatherTaskLog;
|
||||
import org.jeecg.service.WeatherTaskService;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 日志监测线程
|
||||
* @author 86187
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ProgressMonitor{
|
||||
|
||||
private final WeatherTaskService weatherTaskService;
|
||||
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
ProgressMonitorThread monitor = new ProgressMonitorThread();
|
||||
monitor.setName("气象预测日志监测线程");
|
||||
monitor.start();
|
||||
}
|
||||
|
||||
private class ProgressMonitorThread extends Thread{
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for(;;) {
|
||||
try {
|
||||
ProgressEvent event = ProgressQueue.getInstance().take();
|
||||
if(Objects.nonNull(event)) {
|
||||
WeatherTaskLog log = new WeatherTaskLog();
|
||||
log.setTaskId(event.getTaskId());
|
||||
log.setLogContent(event.getContent());
|
||||
weatherTaskService.saveLog(log);
|
||||
}
|
||||
}catch (Exception e){
|
||||
log.error("气象预测日志存储线程异常,日志存储失败,原因为:{}",e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package org.jeecg.task;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* 日志队列
|
||||
*/
|
||||
public class ProgressQueue {
|
||||
|
||||
private final LinkedList<ProgressEvent> queue = new LinkedList<>();
|
||||
|
||||
private static ProgressQueue progressQueue = new ProgressQueue();
|
||||
|
||||
public static ProgressQueue getInstance() {
|
||||
return progressQueue;
|
||||
}
|
||||
|
||||
public void offer(ProgressEvent event) {
|
||||
synchronized (queue) {
|
||||
queue.addLast(event);
|
||||
queue.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public ProgressEvent take(){
|
||||
synchronized (queue) {
|
||||
if(queue.isEmpty()) {
|
||||
try {
|
||||
queue.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
ProgressEvent event = queue.removeFirst();
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,321 @@
|
|||
package org.jeecg.task;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.time.StopWatch;
|
||||
import org.jeecg.common.constant.enums.WeatherDataSourceEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherForecastDatasourceEnum;
|
||||
import org.jeecg.common.constant.enums.WeatherTaskStatusEnum;
|
||||
import org.jeecg.common.exception.JeecgFileUploadException;
|
||||
import org.jeecg.common.properties.SystemStorageProperties;
|
||||
import org.jeecg.common.util.NcUtil;
|
||||
import org.jeecg.modules.base.entity.WeatherData;
|
||||
import org.jeecg.modules.base.entity.WeatherTask;
|
||||
import org.jeecg.modules.base.entity.WeatherTaskLog;
|
||||
import org.jeecg.modules.base.mapper.WeatherDataMapper;
|
||||
import org.jeecg.service.WeatherDataService;
|
||||
import org.jeecg.service.WeatherTaskService;
|
||||
|
||||
import java.io.*;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 气象预测任务执行线程
|
||||
*/
|
||||
public class WeatherTaskExec extends Thread{
|
||||
|
||||
private WeatherTask weatherTask;
|
||||
private WeatherTaskService weatherTaskService;
|
||||
private SystemStorageProperties systemStorageProperties;
|
||||
private WeatherDataMapper weatherDataMapper;
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
public void init(WeatherTask weatherTask,
|
||||
WeatherTaskService weatherTaskService,
|
||||
SystemStorageProperties systemStorageProperties,
|
||||
WeatherDataMapper weatherDataMapper){
|
||||
this.weatherTask = weatherTask;
|
||||
this.weatherTaskService = weatherTaskService;
|
||||
this.systemStorageProperties = systemStorageProperties;
|
||||
this.weatherDataMapper = weatherDataMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
this.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
*/
|
||||
public void execute() {
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
try{
|
||||
//修改任务状态为执行中
|
||||
this.weatherTaskService.updateTaskStatus(this.weatherTask.getId(), WeatherTaskStatusEnum.IN_OPERATION.getKey());
|
||||
//如果此任务已存在历史日志,先清除
|
||||
this.weatherTaskService.deleteTaskLog(this.weatherTask.getId());
|
||||
//执行模拟
|
||||
this.execSimulation();
|
||||
}catch (Exception e){
|
||||
String taskErrorLog = "任务执行失败,原因:"+e.getMessage();
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),taskErrorLog));
|
||||
throw e;
|
||||
}finally {
|
||||
//添加任务耗时
|
||||
stopWatch.stop();
|
||||
long seconds = stopWatch.getTime(TimeUnit.SECONDS);
|
||||
double min = seconds/60D;
|
||||
BigDecimal bgMin = new BigDecimal(min);
|
||||
BigDecimal result = bgMin.setScale(2, RoundingMode.HALF_UP);
|
||||
this.weatherTaskService.updateTaskStatusToCompleted(this.weatherTask.getId(),result.doubleValue());
|
||||
String taskCompletedLog = "任务执行完成,耗时:"+result+"分钟";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),taskCompletedLog));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行模拟
|
||||
*/
|
||||
private void execSimulation(){
|
||||
try {
|
||||
//处理开始日志
|
||||
String forecastModel = "";
|
||||
String startLogFormat = "";
|
||||
String startTimeFormat = LocalDateTimeUtil.format(this.weatherTask.getStartDate(),"yyyy-MM-dd HH:mm:ss");
|
||||
String forecastTime = this.weatherTask.getLeadTime().toString();
|
||||
if (WeatherDataSourceEnum.GRAPHCAST.getKey().equals(this.weatherTask.getPredictionModel())){
|
||||
forecastModel = WeatherDataSourceEnum.GRAPHCAST.getValue();
|
||||
}else if(WeatherDataSourceEnum.PANGU.getKey().equals(this.weatherTask.getPredictionModel())){
|
||||
forecastModel = WeatherDataSourceEnum.PANGU.getValue();
|
||||
}
|
||||
if(this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.CDS.getKey())){
|
||||
startLogFormat = "任务开始,本次使用预测模型为:%s,预测开始时间为:%s,预测时长为:%s,前置数据采用CDS在线数据";
|
||||
}else {
|
||||
startLogFormat = "任务开始,本次使用预测模型为:%s,预测开始时间为:%s,预测时长为:%s,前置数据采用离线数据:"+this.weatherTask.getInputFile();
|
||||
}
|
||||
String startLog = String.format(startLogFormat,forecastModel,startTimeFormat,forecastTime);
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),startLog));
|
||||
|
||||
//处理运行命令
|
||||
List<String> command = new ArrayList<>();
|
||||
String aiModelsPath = systemStorageProperties.getAiModelsPath()+File.separator+"ai-models";
|
||||
String fileName = "panguweather_output_"+this.weatherTask.getId()+".grib";
|
||||
if(this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.CDS.getKey())){
|
||||
command.add(aiModelsPath);
|
||||
command.add("--input");
|
||||
command.add("cds");
|
||||
command.add("--date");
|
||||
command.add(LocalDateTimeUtil.format(this.weatherTask.getStartDate(),"yyyyMMdd"));
|
||||
command.add("--time");
|
||||
command.add(this.weatherTask.getStartTime().toString());
|
||||
command.add("--lead-time");
|
||||
command.add(this.weatherTask.getLeadTime().toString());
|
||||
command.add("--path");
|
||||
command.add(fileName);
|
||||
command.add("--assets");
|
||||
command.add("assets-panguweather");
|
||||
command.add("panguweather");
|
||||
}else if (this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.LOCATION_FILE.getKey())){
|
||||
//离线文件还需处理
|
||||
command.add(aiModelsPath);
|
||||
command.add("--file");
|
||||
command.add(this.weatherTask.getInputFile());
|
||||
command.add("--lead-time");
|
||||
command.add(this.weatherTask.getLeadTime().toString());
|
||||
command.add("--path");
|
||||
command.add(fileName);
|
||||
command.add("--assets");
|
||||
command.add("assets-panguweather");
|
||||
command.add("panguweather");
|
||||
}
|
||||
String execLog = "执行任务命令,开始模拟,命令为:"+command;
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),execLog));
|
||||
ProcessBuilder processBuilder = new ProcessBuilder(command);
|
||||
processBuilder.directory(new File(systemStorageProperties.getPanguModelExecPath()));
|
||||
processBuilder.redirectErrorStream(true);
|
||||
Process process = processBuilder.start();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
|
||||
//读取输出日志
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if(StrUtil.isNotBlank(line)){
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),line));
|
||||
}
|
||||
}
|
||||
//等待进程结束
|
||||
process.waitFor();
|
||||
|
||||
String handleGribLog = "预测结束,开始处理grib文件";
|
||||
ProgressQueue.getInstance().offer(new ProgressEvent(this.weatherTask.getId(),handleGribLog));
|
||||
LocalDateTime startTime = this.weatherTask.getStartDate().atTime(this.weatherTask.getStartTime(), 0, 0);
|
||||
//grib_copy命令
|
||||
String gribCopyCommandPath = systemStorageProperties.getAiModelsPath()+File.separator+"grib_copy";
|
||||
//grib_set命令
|
||||
String gribSetCommandPath = systemStorageProperties.getAiModelsPath()+File.separator+"grib_set";
|
||||
//需删除文件
|
||||
List<String> delFiles = new ArrayList<>();
|
||||
//需移动文件
|
||||
List<String> moveFiles = new ArrayList<>();
|
||||
//公用gribProcessBuilder
|
||||
ProcessBuilder gribProcessBuilder = new ProcessBuilder();
|
||||
gribProcessBuilder.directory(new File(systemStorageProperties.getPanguModelExecPath()));
|
||||
//定义名称后缀,执行grib_set命令时去除,否则命名会冲突
|
||||
String gribFileSuffix = "_not_grib_set";
|
||||
//把grib文件切割成每6小时一份
|
||||
int step = 6;
|
||||
int i=this.weatherTask.getStartTime();
|
||||
while(i <= this.weatherTask.getLeadTime()){
|
||||
String gribCopyFileName = "panguweather_"+LocalDateTimeUtil.format(startTime,"yyyyMMddHH")+gribFileSuffix+".grib";
|
||||
delFiles.add(gribCopyFileName);
|
||||
//切割grib文件命令
|
||||
List<String> gribCopyCommand = new ArrayList<>();
|
||||
gribCopyCommand.add(gribCopyCommandPath);
|
||||
gribCopyCommand.add("-w");
|
||||
gribCopyCommand.add("step="+i);
|
||||
gribCopyCommand.add(fileName);
|
||||
gribCopyCommand.add(gribCopyFileName);
|
||||
gribProcessBuilder.command(gribCopyCommand);
|
||||
Process gribCopyProcess = gribProcessBuilder.start();
|
||||
gribCopyProcess.waitFor();
|
||||
|
||||
//重新设置reftime信息
|
||||
String gribSetFileName = "panguweather_"+LocalDateTimeUtil.format(startTime,"yyyyMMddHH")+".grib";
|
||||
moveFiles.add(gribSetFileName);
|
||||
String date = LocalDateTimeUtil.format(startTime,"yyyyMMdd");
|
||||
String time = LocalDateTimeUtil.format(startTime,"HHmm");
|
||||
List<String> gribSetCommand = new ArrayList<>();
|
||||
gribSetCommand.add(gribSetCommandPath);
|
||||
gribSetCommand.add("-s");
|
||||
gribSetCommand.add("dataDate="+date+",dataTime="+time+",endStep="+0);
|
||||
gribSetCommand.add(gribCopyFileName);
|
||||
gribSetCommand.add(gribSetFileName);
|
||||
gribProcessBuilder.command(gribSetCommand);
|
||||
Process gribSetProcess = gribProcessBuilder.start();
|
||||
gribSetProcess.waitFor();
|
||||
i+=step;
|
||||
startTime = startTime.plusHours(step);
|
||||
}
|
||||
if (CollUtil.isNotEmpty(moveFiles)) {
|
||||
for (String moveFile : moveFiles) {
|
||||
File srcFile = new File(systemStorageProperties.getPanguModelExecPath()+File.separator+moveFile);
|
||||
if (srcFile.exists()) {
|
||||
File targetFile = new File(this.getPanguWeatherPath()+File.separator+File.separator+moveFile);
|
||||
FileUtil.move(srcFile,targetFile,true);
|
||||
//保存生成的气象数据存储到数据库
|
||||
this.saveGribInfoToDB(targetFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
//删除gribCopy产生的文件
|
||||
for (String delFile : delFiles) {
|
||||
File srcFile = new File(systemStorageProperties.getPanguModelExecPath()+File.separator+delFile);
|
||||
if (srcFile.exists()) {
|
||||
srcFile.delete();
|
||||
}
|
||||
}
|
||||
//删除预测的结果文件
|
||||
String outputFilePath = systemStorageProperties.getPanguModelExecPath()+File.separator+fileName;
|
||||
File outputFile = new File(outputFilePath);
|
||||
if(outputFile.exists()){
|
||||
outputFile.delete();
|
||||
}
|
||||
|
||||
//如果是本地文件进行预测,预测结束后删除文件
|
||||
if(this.weatherTask.getDataSources().equals(WeatherForecastDatasourceEnum.LOCATION_FILE.getKey())){
|
||||
String inputFilePath = systemStorageProperties.getPanguModelExecPath()+File.separator+this.weatherTask.getInputFile();
|
||||
File inputFile = new File(inputFilePath);
|
||||
if(inputFile.exists()){
|
||||
inputFile.delete();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存生成的气象数据存储到数据库
|
||||
* @param file
|
||||
*/
|
||||
private void saveGribInfoToDB(File file){
|
||||
//获取文件数据开始日期
|
||||
String reftime = NcUtil.getReftime(file.getAbsolutePath());
|
||||
if(StringUtils.isBlank(reftime)) {
|
||||
throw new JeecgFileUploadException("解析气象文件起始时间数据异常,此文件可能损坏");
|
||||
}
|
||||
//计算文件大小M
|
||||
BigDecimal divideVal = new BigDecimal("1024");
|
||||
BigDecimal bg = new BigDecimal(file.length());
|
||||
BigDecimal fileSize = bg.divide(divideVal).divide(divideVal).setScale(2, RoundingMode.HALF_UP);
|
||||
//处理文件数据开始时间
|
||||
Instant instant = Instant.parse(reftime);
|
||||
LocalDateTime utcDateTime = LocalDateTime.ofInstant(instant, ZoneId.of("UTC"));
|
||||
//计算文件MD5值
|
||||
String md5Val = "";
|
||||
try (InputStream is = new FileInputStream(file.getAbsolutePath())) {
|
||||
md5Val = DigestUtils.md5Hex(is);
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(file.getName()+"MD5值计算失败");
|
||||
}
|
||||
//校验文件是否存在,存在删除从新新增
|
||||
LambdaQueryWrapper<WeatherData> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(WeatherData::getFileName,file.getName());
|
||||
WeatherData queryResult = weatherDataMapper.selectOne(queryWrapper);
|
||||
if(Objects.nonNull(queryResult)){
|
||||
weatherDataMapper.deleteById(queryResult.getId());
|
||||
}
|
||||
//构建文件信息
|
||||
WeatherData weatherData = new WeatherData();
|
||||
weatherData.setFileName(file.getName());
|
||||
weatherData.setFileSize(fileSize.doubleValue());
|
||||
weatherData.setFileExt(file.getName().substring(file.getName().lastIndexOf(".")+1));
|
||||
weatherData.setDataStartTime(utcDateTime);
|
||||
weatherData.setDataSource(weatherTask.getPredictionModel());
|
||||
weatherData.setFilePath(file.getAbsolutePath());
|
||||
weatherData.setMd5Value(md5Val);
|
||||
weatherData.setShareTotal(1);
|
||||
weatherDataMapper.insert(weatherData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取盘古数据存储路径
|
||||
* @return
|
||||
*/
|
||||
private String getPanguWeatherPath(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.systemStorageProperties.getRootPath());
|
||||
sb.append(File.separator);
|
||||
sb.append(this.systemStorageProperties.getPangu());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取盘古数据存储路径
|
||||
* @return
|
||||
*/
|
||||
private String getGraphcastWeatherPath(){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(this.systemStorageProperties.getRootPath());
|
||||
sb.append(File.separator);
|
||||
sb.append(this.systemStorageProperties.getGraphcast());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package org.jeecg.vo;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 预测文件上传VO
|
||||
*/
|
||||
@Data
|
||||
public class ForecastFileVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 任务id
|
||||
*/
|
||||
private String taskId;
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*/
|
||||
@JSONField(serialize = false)
|
||||
private MultipartFile file;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user