1.完成气象预测任务新增、修改、删除、分页查询、查询任务日志功能

2.完成气象数据管理功能文件校验、文件上传、文件删除功能
3.解决cloud服务数据传递功能
This commit is contained in:
panbaolin 2025-08-14 19:26:12 +08:00
parent dd27760a00
commit e5ad6fd156
55 changed files with 2301 additions and 4658 deletions

File diff suppressed because one or more lines are too long

View File

@ -39,6 +39,11 @@
</repository>
</repositories>
<properties>
<netcdf.version>4.5.5</netcdf.version>
<grib.version>4.5.5</grib.version>
</properties>
<dependencies>
<!--jeecg-tools-->
<dependency>
@ -320,5 +325,16 @@
<groupId>org.jeecgframework.boot3</groupId>
<artifactId>minidao-spring-boot-starter-jsqlparser-4.9</artifactId>
</dependency>
<!-- 读取 .nc 文件 -->
<dependency>
<groupId>edu.ucar</groupId>
<artifactId>netcdf4</artifactId>
<version>${netcdf.version}</version>
</dependency>
<dependency>
<groupId>edu.ucar</groupId>
<artifactId>grib</artifactId>
<version>${grib.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -33,23 +33,6 @@ public interface ServiceNameConstants {
* 微服务名 demo模块
*/
String SERVICE_DEMO = "jeecg-demo";
/**
* 微服务名joa模块
*/
String SERVICE_JOA = "jeecg-joa";
// /**
// * 微服务名online在线模块
// */
// String SERVICE_ONLINE = "jeecg-online";
// /**
// * 微服务名OA模块
// */
// String SERVICE_EOA = "jeecg-eoa";
// /**
// * 微服务名表单设计模块
// */
// String SERVICE_FORM = "jeecg-desform";
/**
* gateway通过header传递根路径 basePath

View File

@ -0,0 +1,30 @@
package org.jeecg.common.constant.enums;
/**
* 气象数据来源说明枚举
*/
public enum WeatherDataSourceEnum {
/**
* 盘古模型
*/
PANGU(1),
/**
* Graphcast
*/
GRAPHCAST(2),
/**
* 再分析数据
*/
RE_ANALYSIS(3);
private Integer key;
WeatherDataSourceEnum(Integer key) {
this.key = key;
}
public Integer getKey(){
return this.key;
}
}

View File

@ -0,0 +1,37 @@
package org.jeecg.common.constant.enums;
/**
* 文件类型说明枚举
*/
public enum WeatherFilePrefixEnum {
FORECAST(0, "panguweather_");
private Integer key;
private String value;
WeatherFilePrefixEnum(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 (WeatherFilePrefixEnum prefix : WeatherFilePrefixEnum.values()) {
if (prefix.getKey() == key) {
return prefix.getValue();
}
}
return null;
}
}

View File

@ -0,0 +1,21 @@
package org.jeecg.common.constant.enums;
/**
* 文件类型说明枚举
*/
public enum WeatherFileSuffixEnum {
GRIB("grib"),GRIB2("grib2");;
private String value;
WeatherFileSuffixEnum(String value) {
this.value = value;
}
public String getValue(){
return this.value;
}
}

View File

@ -0,0 +1,26 @@
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;
}
}

View File

@ -0,0 +1,30 @@
package org.jeecg.common.constant.enums;
/**
* 预测任务状态说明枚举
*/
public enum WeatherTaskStatusEnum {
/**
* 未开始
*/
NOT_STARTED(0),
/**
* 未开始
*/
IN_OPERATION(1),
/**
* 已完成
*/
COMPLETED(2);
private Integer key;
WeatherTaskStatusEnum(Integer key) {
this.key = key;
}
public Integer getKey(){
return this.key;
}
}

View File

@ -0,0 +1,32 @@
package org.jeecg.common.constant.enums;
/**
* 文件类型说明枚举
*/
public enum WeatherTypeEnum {
TEMPERATURE(0, "panguweather_"),
PRESSURE(1, "PRESSURE"),
HUMIDITY(2, "HUMIDITY"),
WIND(3, "WIND"),
PRECIPITATION(4, "PRECIPITATION");
private Integer key;
private String value;
WeatherTypeEnum(Integer key, String value) {
this.key = key;
this.value = value;
}
public Integer getKey(){
return this.key;
}
public String getValue(){
return this.value;
}
}

View File

@ -0,0 +1,31 @@
package org.jeecg.common.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "filesystem")
public class SystemStorageProperties {
/**
* 系统存储根路径
*/
private String rootPath;
/**
* 盘古模型预测数据存储路径
*/
private String pangu;
/**
* graphcast模型预测数据存储路径
*/
private String graphcast;
/**
* 再分析数据存储路径
*/
private String reAnalysis;
}

View File

@ -3,6 +3,10 @@ package org.jeecg.common.system.base.entity;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import org.jeecg.common.validgroup.InsertGroup;
import org.jeecg.common.validgroup.UpdateGroup;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
@ -23,12 +27,14 @@ import lombok.experimental.Accessors;
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class JeecgEntity implements Serializable {
public class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@Null(message = "ID必须为空",groups = { InsertGroup.class})
@NotNull(message = "ID不能为空",groups = { UpdateGroup.class})
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "ID")
private java.lang.String id;

View File

@ -1,6 +1,6 @@
package org.jeecg.common.system.base.service.impl;
import org.jeecg.common.system.base.entity.JeecgEntity;
import org.jeecg.common.system.base.entity.BaseEntity;
import org.jeecg.common.system.base.service.JeecgService;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
@ -14,6 +14,6 @@ import lombok.extern.slf4j.Slf4j;
* @Version: 1.0
*/
@Slf4j
public class JeecgServiceImpl<M extends BaseMapper<T>, T extends JeecgEntity> extends ServiceImpl<M, T> implements JeecgService<T> {
public class JeecgServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends ServiceImpl<M, T> implements JeecgService<T> {
}

View File

@ -0,0 +1,67 @@
package org.jeecg.common.system.query;
import cn.hutool.core.util.StrUtil;
public class PageRequest {
/** 当前记录起始索引 */
private Integer pageNum;
/** 每页显示记录数 */
private Integer pageSize;
/** 排序列 */
private String orderByColumn;
/** 排序的方向desc或者asc */
private String isAsc = "asc";
public PageRequest() {
this.pageNum = 1;
this.pageSize = 10;
}
public Integer getPageNum() {
return pageNum;
}
public void setPageNum(Integer pageNum) {
this.pageNum = pageNum;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public String getOrderByColumn() {
return orderByColumn;
}
public void setOrderByColumn(String orderByColumn) {
this.orderByColumn = orderByColumn;
}
public String getIsAsc() {
return isAsc;
}
public void setIsAsc(String isAsc) {
if (StrUtil.isNotEmpty(isAsc))
{
// 兼容前端排序类型
if ("ascending".equals(isAsc))
{
isAsc = "asc";
}
else if ("descending".equals(isAsc))
{
isAsc = "desc";
}
this.isAsc = isAsc;
}
}
}

View File

@ -0,0 +1,254 @@
package org.jeecg.common.util;
import ucar.ma2.Array;
import ucar.ma2.ArrayDouble;
import ucar.ma2.DataType;
import ucar.ma2.Index;
import ucar.nc2.Attribute;
import ucar.nc2.NetcdfFile;
import ucar.nc2.NetcdfFileWriter;
import ucar.nc2.Variable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public final class NcUtil {
private static final Logger logger = LoggerFactory.getLogger(NcUtil.class);
private static final List<String> SPECIAL_VARS = List.of("CO", "ASIJ");
private static final double ROUNDING_FACTOR = 1000d;
private NcUtil() {
throw new IllegalStateException("Utility class");
}
/**
* 读取三维数据如果为四维数据层级固定读取第一层数据降为三维数据
*/
public static List<List<List<Double>>> getNCByName(NetcdfFile ncfile, String name, int layer) {
Objects.requireNonNull(ncfile, "NetcdfFile cannot be null");
Objects.requireNonNull(name, "Variable name cannot be null");
try {
Variable variable = ncfile.findVariable(name);
Array data = variable.read();
int[] shape = data.getShape();
Index index = data.getIndex();
List<List<List<Double>>> resultAll = new ArrayList<>(shape[0]);
if (shape.length == 3) {
process3DData(data, index, shape, name, resultAll);
} else {
process4DData(data, index, shape, name, layer, resultAll);
}
return resultAll;
} catch (IOException e) {
logger.error("Error reading variable {} from NetCDF file", name, e);
throw new RuntimeException("Failed to read NetCDF data", e);
}
}
/**
* 获取一维NC数据
*/
public static List<Double> getNCList(NetcdfFile ncFile, String name) {
Objects.requireNonNull(ncFile, "NetcdfFile cannot be null");
Objects.requireNonNull(name, "Variable name cannot be null");
try {
Variable variable = ncFile.findVariable(name);
Array data = variable.read();
int[] shape = data.getShape();
Index index = data.getIndex();
List<Double> resultAll = new ArrayList<>(shape[0]);
for (int k = 0; k < shape[0]; k++) {
resultAll.add(processValue(data.getDouble(index.set(k)), name));
}
return resultAll;
} catch (IOException e) {
logger.error("Error reading variable {} from NetCDF file", name, e);
throw new RuntimeException("Failed to read NetCDF data", e);
}
}
private static void process3DData(Array data, Index index, int[] shape, String name,
List<List<List<Double>>> resultAll) {
for (int i = 0; i < shape[0]; i++) {
List<List<Double>> resultPiece = new ArrayList<>(shape[1]);
for (int j = 0; j < shape[1]; j++) {
List<Double> strings = new ArrayList<>(shape[2]);
for (int k = 0; k < shape[2]; k++) {
strings.add(processValue(data.getDouble(index.set(i, j, k)), name));
}
resultPiece.add(strings);
}
resultAll.add(resultPiece);
}
}
private static void process4DData(Array data, Index index, int[] shape, String name,
int layer, List<List<List<Double>>> resultAll) {
for (int i = 0; i < shape[0]; i++) {
List<List<Double>> resultPiece = new ArrayList<>(shape[2]);
for (int j = 0; j < shape[2]; j++) {
List<Double> strings = new ArrayList<>(shape[3]);
for (int k = 0; k < shape[3]; k++) {
strings.add(processValue(data.getDouble(index.set(i, layer, j, k)), name));
}
resultPiece.add(strings);
}
resultAll.add(resultPiece);
}
}
/**
* 特殊处理最多读取24小时14层数据四维数据
*/
public static List<List<List<List<Double>>>> getNCByName(NetcdfFile ncFile, String name) {
return getNCDataWithFullLayers(ncFile, name, false);
}
/**
* 完整读取所有四维数据
*/
public static List<List<List<List<Double>>>> getNCAllTimeByName(NetcdfFile ncFile, String name) {
return getNCDataWithFullLayers(ncFile, name, true);
}
private static List<List<List<List<Double>>>> getNCDataWithFullLayers(NetcdfFile ncFile,
String name,
boolean allTime) {
Objects.requireNonNull(ncFile, "NetcdfFile cannot be null");
Objects.requireNonNull(name, "Variable name cannot be null");
try {
Variable variable = ncFile.findVariable(name);
Array data = variable.read();
int[] shape = data.getShape();
Index index = data.getIndex();
List<List<List<List<Double>>>> resultAll = new ArrayList<>(shape[0]);
for (int i = 0; i < shape[0]; i++) {
List<List<List<Double>>> resultLayer = new ArrayList<>(shape[1]);
for (int l = 0; l < shape[1]; l++) {
List<List<Double>> resultPiece = new ArrayList<>(shape[2]);
for (int j = 0; j < shape[2]; j++) {
List<Double> strings = new ArrayList<>(shape[3]);
for (int k = 0; k < shape[3]; k++) {
strings.add(processValue(data.getDouble(index.set(i, l, j, k)), name));
}
resultPiece.add(strings);
}
resultLayer.add(resultPiece);
}
resultAll.add(resultLayer);
}
return resultAll;
} catch (IOException e) {
logger.error("Error reading variable {} from NetCDF file", name, e);
throw new RuntimeException("Failed to read NetCDF data", e);
}
}
/**
* 获取NC文件维度信息
*/
public static int[] getShapeByName(NetcdfFile ncfile, String name) {
Objects.requireNonNull(ncfile, "NetcdfFile cannot be null");
Objects.requireNonNull(name, "Variable name cannot be null");
try {
Variable variable = ncfile.findVariable(name);
Array data = variable.read();
return data.getShape();
} catch (IOException e) {
logger.error("Error getting shape for variable {} from NetCDF file", name, e);
throw new RuntimeException("Failed to get NetCDF shape", e);
}
}
/**
* 生成NC文件并支持持续写入文件
*/
public static void genNcFile(List<List<List<List<Double>>>> dataList,
String newNcFilePath,
String variableName) {
Objects.requireNonNull(dataList, "Data list cannot be null");
Objects.requireNonNull(newNcFilePath, "File path cannot be null");
Objects.requireNonNull(variableName, "Variable name cannot be null");
try {
File file = new File(newNcFilePath);
boolean fileExists = file.exists();
// 数据维度
int tstepNum = dataList.size();
int layNum = dataList.get(0).size();
int rowNum = dataList.get(0).get(0).size();
int colNum = dataList.get(0).get(0).get(0).size();
NetcdfFileWriter ncWriter = fileExists ?
NetcdfFileWriter.openExisting(newNcFilePath) :
NetcdfFileWriter.createNew(NetcdfFileWriter.Version.netcdf3, newNcFilePath);
if (fileExists) {
ncWriter.setRedefineMode(true);
} else {
// 定义维度
ncWriter.addDimension(null, "TSTEP", tstepNum);
ncWriter.addDimension(null, "LAY", layNum);
ncWriter.addDimension(null, "ROW", rowNum);
ncWriter.addDimension(null, "COL", colNum);
ncWriter.addDimension(null, "LAYER", 1);
}
Variable dataVar = ncWriter.addVariable(null, variableName, DataType.DOUBLE,
variableName.contains("AERDEP") ? "TSTEP LAYER ROW COL" : "TSTEP LAY ROW COL");
// 关联维度到变量
ncWriter.addVariableAttribute(dataVar, new Attribute("units", "none"));
ncWriter.addVariableAttribute(dataVar, new Attribute("long_name", variableName));
if (fileExists) {
ncWriter.setRedefineMode(false);
} else {
ncWriter.create();
}
// 将数据转换为NetCDF数组
ArrayDouble.D4 dataArray = new ArrayDouble.D4(tstepNum, layNum, rowNum, colNum);
for (int i = 0; i < tstepNum; i++) {
List<List<List<Double>>> layData = dataList.get(i);
for (int j = 0; j < layNum; j++) {
List<List<Double>> rowData = layData.get(j);
for (int r = 0; r < rowNum; r++) {
List<Double> colData = rowData.get(r);
for (int c = 0; c < colNum; c++) {
dataArray.set(dataArray.getIndex().set(i, j, r, c), colData.get(c));
}
}
}
}
ncWriter.write(dataVar, dataArray);
ncWriter.close();
} catch (Exception e) {
logger.error("Error generating NetCDF file {}", newNcFilePath, e);
throw new RuntimeException("Failed to generate NetCDF file", e);
}
}
private static double processValue(double value, String name) {
return SPECIAL_VARS.contains(name) ? value :
Math.ceil(value * ROUNDING_FACTOR) / ROUNDING_FACTOR;
}
}

View File

@ -0,0 +1,7 @@
package org.jeecg.common.validgroup;
/**
* 新增验证组
*/
public interface InsertGroup {
}

View File

@ -0,0 +1,4 @@
package org.jeecg.common.validgroup;
public interface UpdateGroup {
}

View File

@ -3,6 +3,7 @@ package org.jeecg.config.security;
import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;

View File

@ -0,0 +1,99 @@
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 lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
/**
* 气象数据文件表
*/
@Data
@TableName("stas_weather_data")
public class WeatherData {
/**
* ID
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "ID")
private String id;
/**
* 文件名称
*/
@TableField(value = "file_name")
private String fileName;
/**
* 文件大小
*/
@TableField(value = "file_size")
private Double fileSize;
/**
* 文件类型
*/
@TableField(value = "file_ext")
private String fileExt;
/**
* 气象数据开始时间
*/
@TableField(value = "data_start_time")
private LocalDateTime dataStartTime;
/**
* 气象数据结束时间
*/
@TableField(value = "data_end_time")
private LocalDateTime dataEndTime;
/**
* 数据来源1-盘古模型2-graphcast3-再分析数据
*/
@TableField(value = "data_source")
private Integer dataSource;
/**
* 文件存储路径
*/
@TableField(value = "file_path")
private String filePath;
/**
* 创建人
*/
@TableField(value = "create_by")
private String createBy;
/**
* 创建时间
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(value = "create_time")
private Date createTime;
/**
* 文件MD5值
*/
@TableField(value = "md5_value")
private String md5Value;
/**
* 总分片数
*/
@TableField(value = "share_total")
private Integer shareTotal;
/**
* 分片索引
*/
@TableField(value = "share_index")
private Integer shareIndex;
}

View File

@ -0,0 +1,85 @@
package org.jeecg.modules.base.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
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 java.time.LocalDate;
/**
* 天气预测任务表
*/
@Data
@TableName("stas_weather_task")
public class WeatherTask extends BaseEntity {
/**
* 任务名称
*/
@NotBlank(message = "任务名称不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "task_name")
private String taskName;
/**
* 任务状态0-未开始1-运行中2-已完成
*/
@Null(message = "任务状态必须为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "task_status")
private Integer taskStatus;
/**
* 耗时小时默认0
*/
@Null(message = "耗时参数必须为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "time_consuming")
private Integer timeConsuming;
/**
* 预测模型1-盘古2-Graphcast
*/
@NotNull(message = "预测模型不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "prediction_model")
private Integer predictionModel;
/**
* 开始预测日期
*/
@NotNull(message = "预测开始日期不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "start_date")
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate startDate;
/**
* 开始预测时间
*/
@NotNull(message = "预测开始时间不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "start_time")
private Integer startTime;
/**
* 预测时长
*/
@NotNull(message = "预测时长不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "lead_time")
private Integer leadTime;
/**
* 数据来源1-cds2-本地文件
*/
@NotNull(message = "数据来源不能为空",groups = {InsertGroup.class, UpdateGroup.class})
@TableField(value = "data_sources")
private Integer dataSources;
/**
* 如果data_sources为2则存储本地文件路径
*/
@TableField(value = "input_file")
private String inputFile;
}

View File

@ -0,0 +1,44 @@
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 lombok.Data;
import java.time.LocalDateTime;
/**
* 天气预测任务日志表
*/
@Data
@TableName("stas_weather_task_log")
public class WeatherTaskLog{
/**
* ID
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "ID")
private String id;
/**
* 任务表主键
*/
@TableField(value = "task_id")
private String taskId;
/**
* 任务运行过程日志
*/
@TableField(value = "log_content")
private String logContent;
/**
* 创建时间
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,8 @@
package org.jeecg.modules.base.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.base.entity.WeatherData;
public interface WeatherDataMapper extends BaseMapper<WeatherData> {
}

View File

@ -0,0 +1,8 @@
package org.jeecg.modules.base.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.base.entity.WeatherTaskLog;
public interface WeatherTaskLogMapper extends BaseMapper<WeatherTaskLog> {
}

View File

@ -0,0 +1,8 @@
package org.jeecg.modules.base.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.base.entity.WeatherTask;
public interface WeatherTaskMapper extends BaseMapper<WeatherTask> {
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.base.mapper.WeatherDataMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.base.mapper.WeatherTaskLogMapper">
</mapper>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.base.mapper.WeatherTaskMapper">
</mapper>

View File

@ -3,10 +3,6 @@ package org.jeecg.modules.base.service;
import org.jeecg.common.api.dto.LogDTO;
import org.jeecg.common.system.vo.LoginUser;
/**
* common接口
* @author: jeecg-boot
*/
public interface BaseCommonService {
/**

View File

@ -18,10 +18,6 @@ import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import java.util.*;
/**
* @Description: common实现类
* @author: jeecg-boot
*/
@Service
@Slf4j
public class BaseCommonServiceImpl implements BaseCommonService {

View File

@ -10,6 +10,7 @@ import org.jeecg.common.constant.enums.EmailTemplateEnum;
import org.jeecg.common.desensitization.annotation.SensitiveDecode;
import org.jeecg.common.system.api.factory.SysBaseAPIFallbackFactory;
import org.jeecg.common.system.vo.*;
import org.jeecg.config.FeignConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
@ -29,7 +30,7 @@ import java.util.Set;
* @author: jeecg-boot
*/
@Component
@FeignClient(contextId = "sysBaseRemoteApi", value = ServiceNameConstants.SERVICE_SYSTEM, fallbackFactory = SysBaseAPIFallbackFactory.class)
@FeignClient(contextId = "sysBaseRemoteApi", value = ServiceNameConstants.SERVICE_SYSTEM, fallbackFactory = SysBaseAPIFallbackFactory.class,configuration = FeignConfig.class)
@ConditionalOnMissingClass("org.jeecg.modules.system.service.impl.SysBaseApiImpl")
public interface ISysBaseAPI extends CommonAPI {

View File

@ -1,180 +1,52 @@
//package org.jeecg.config;
//
//import java.io.IOException;
//import java.util.ArrayList;
//import java.util.Arrays;
//import java.util.List;
//import java.util.SortedMap;
//
//import jakarta.servlet.http.HttpServletRequest;
//
//import org.jeecg.common.config.mqtoken.UserTokenContext;
//import org.jeecg.common.constant.CommonConstant;
//import org.jeecg.common.util.DateUtils;
//import org.jeecg.common.util.PathMatcherUtil;
//import org.jeecg.config.sign.interceptor.SignAuthConfiguration;
//import org.jeecg.config.sign.util.HttpUtils;
//import org.jeecg.config.sign.util.SignUtil;
//import org.springframework.beans.factory.ObjectFactory;
//import org.springframework.boot.autoconfigure.AutoConfigureBefore;
//import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
//import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
//import org.springframework.cloud.openfeign.FeignAutoConfiguration;
//import org.springframework.cloud.openfeign.support.SpringDecoder;
//import org.springframework.cloud.openfeign.support.SpringEncoder;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.context.annotation.Primary;
//import org.springframework.context.annotation.Scope;
//import org.springframework.http.MediaType;
//import org.springframework.web.context.request.RequestContextHolder;
//import org.springframework.web.context.request.ServletRequestAttributes;
//
//import com.alibaba.fastjson.JSON;
//import com.alibaba.fastjson.serializer.SerializerFeature;
//import com.alibaba.fastjson.support.config.FastJsonConfig;
//import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
//import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
//
//import feign.Feign;
//import feign.Logger;
//import feign.RequestInterceptor;
//import feign.codec.Decoder;
//import feign.codec.Encoder;
//import feign.form.spring.SpringFormEncoder;
//import lombok.extern.slf4j.Slf4j;
//
///**
// * @Description: FeignConfig
// * @author: JeecgBoot
// */
//@ConditionalOnClass(Feign.class)
//@AutoConfigureBefore(FeignAutoConfiguration.class)
//@Slf4j
//@Configuration
//public class FeignConfig {
//
package org.jeecg.config;
import feign.Feign;
import feign.RequestTemplate;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.http.HttpHeaders;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import feign.Logger;
import feign.RequestInterceptor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Configuration
public class FeignConfig implements RequestInterceptor{
/**
* @param requestTemplate
*/
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
requestTemplate.header("Authorization", request.getHeader("Authorization"));
}
// /**
// * 设置feign header参数
// * X_ACCESS_TOKENX_SIGNX_TIMESTAMP
// * @return
// */
// @Bean
// @ConditionalOnMissingBean(RequestInterceptor.class)
// public RequestInterceptor requestInterceptor() {
// return requestTemplate -> {
// return template -> {
// ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// if (null != attributes) {
// if (attributes != null) {
// HttpServletRequest request = attributes.getRequest();
// log.debug("Feign request: {}", request.getRequestURI());
// // 将token信息放入header中
// String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
// if(token==null || "".equals(token)){
// token = request.getParameter("token");
// }
// String token = request.getHeader(HttpHeaders.AUTHORIZATION);
// log.info("Feign Login Request token: {}", token);
// requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
// }else{
// //解决后台任务MQ中调用feign接口无会话token的问题
// String token = UserTokenContext.getToken();
// log.info("Feign No Login token: {}", token);
// requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
// template.header(HttpHeaders.AUTHORIZATION, token);
// }
//
// //================================================================================================================
// //针对特殊接口进行加签验证 根据URL地址过滤请求 字典表参数签名验证
// if (PathMatcherUtil.matches(Arrays.asList(SignAuthConfiguration.SIGN_URL_LIST),requestTemplate.path())) {
// try {
// log.info("============================ [begin] fegin api url ============================");
// log.info(requestTemplate.path());
// log.info(requestTemplate.method());
// String queryLine = requestTemplate.queryLine();
// String questionMark="?";
// if(queryLine!=null && queryLine.startsWith(questionMark)){
// queryLine = queryLine.substring(1);
// }
// log.info(queryLine);
// if(requestTemplate.body()!=null){
// log.info(new String(requestTemplate.body()));
// }
// SortedMap<String, String> allParams = HttpUtils.getAllParams(requestTemplate.path(),queryLine,requestTemplate.body(),requestTemplate.method());
// String sign = SignUtil.getParamsSign(allParams);
// log.info(" Feign request params sign: {}",sign);
// log.info("============================ [end] fegin api url ============================");
// requestTemplate.header(CommonConstant.X_SIGN, sign);
// requestTemplate.header(CommonConstant.X_TIMESTAMP, String.valueOf(System.currentTimeMillis()));
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
// //================================================================================================================
// };
// }
//
//
//
// /**
// * Feign 客户端的日志记录默认级别为NONE
// * Logger.Level 的具体级别如下
// * NONE不记录任何信息
// * BASIC仅记录请求方法URL以及响应状态码和执行时间
// * HEADERS除了记录 BASIC级别的信息外还会记录请求和响应的头信息
// * FULL记录所有请求与响应的明细包括头信息请求体元数据
// */
// @Bean
// Logger.Level feignLoggerLevel() {
// return Logger.Level.FULL;
// }
//
// /**
// * Feign支持文件上传
// * @param messageConverters
// * @return
// */
// @Bean
// @Primary
// @Scope("prototype")
// public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
// return new SpringFormEncoder(new SpringEncoder(messageConverters));
// }
//
// // update-begin--Author:sunjianlei Date:20210604 for Feign 添加 FastJson 的解析支持 ----------
// /**
// * Feign 添加 FastJson 的解析支持
// */
// @Bean
// public Encoder feignEncoder() {
// return new SpringEncoder(feignHttpMessageConverter());
// }
//
// @Bean("apiFeignDecoder")
// public Decoder feignDecoder() {
// return new SpringDecoder(feignHttpMessageConverter());
// }
//
// /**
// * 设置解码器为fastjson
// *
// * @return
// */
// private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
// final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
// return () -> httpMessageConverters;
// }
//
// private FastJsonHttpMessageConverter getFastJsonConverter() {
// FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
//
// List<MediaType> supportedMediaTypes = new ArrayList<>();
// MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE);
// supportedMediaTypes.add(mediaTypeJson);
// converter.setSupportedMediaTypes(supportedMediaTypes);
// FastJsonConfig config = new FastJsonConfig();
// config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
// config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
// converter.setFastJsonConfig(config);
//
// return converter;
// }
// // update-end--Author:sunjianlei Date:20210604 for Feign 添加 FastJson 的解析支持 ----------
//
//}
}

View File

@ -21,6 +21,7 @@
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-base-core</artifactId>
<version>${jeecgboot.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -14,7 +14,7 @@
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-system-local-api</artifactId>
<version>3.8.1</version>
<version>${jeecgboot.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>

View File

@ -1,13 +1,11 @@
package org.jeecg.modules.message.entity;
import org.jeecg.common.aspect.annotation.Dict;
import org.jeecg.common.system.base.entity.JeecgEntity;
import org.jeecg.common.system.base.entity.BaseEntity;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -22,7 +20,7 @@ import lombok.experimental.Accessors;
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_sms")
public class SysMessage extends JeecgEntity {
public class SysMessage extends BaseEntity {
/**推送内容*/
@Excel(name = "推送内容", width = 15)
private java.lang.String esContent;

View File

@ -1,6 +1,6 @@
package org.jeecg.modules.message.entity;
import org.jeecg.common.system.base.entity.JeecgEntity;
import org.jeecg.common.system.base.entity.BaseEntity;
import org.jeecgframework.poi.excel.annotation.Excel;
import com.baomidou.mybatisplus.annotation.TableName;
@ -19,7 +19,7 @@ import lombok.experimental.Accessors;
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("sys_sms_template")
public class SysMessageTemplate extends JeecgEntity{
public class SysMessageTemplate extends BaseEntity{
/**模板CODE*/
@Excel(name = "模板CODE", width = 15)
private java.lang.String templateCode;

View File

@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.jeecg.common.system.base.entity.JeecgEntity;
import org.jeecg.common.system.base.entity.BaseEntity;
import org.jeecgframework.poi.excel.annotation.Excel;
/**
@ -15,7 +15,7 @@ import org.jeecgframework.poi.excel.annotation.Excel;
@TableName("oss_file")
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OssFile extends JeecgEntity {
public class OssFile extends BaseEntity {
private static final long serialVersionUID = 1L;

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-parent</artifactId>
<version>3.8.1</version>
</parent>
<artifactId>jeecg-module-weather</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-base-core</artifactId>
<version>${jeecgboot.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,87 @@
package org.jeecg.controller;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
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.WeatherFileSuffixEnum;
import org.jeecg.service.WeatherDataService;
import org.jeecg.vo.FileExistVo;
import org.jeecg.vo.FileUploadResultVo;
import org.jeecg.vo.FileVo;
import org.jeecg.vo.WeatherResultVO;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.List;
@Validated
@RestController
@RequestMapping("weatherData")
@RequiredArgsConstructor
public class WeatherDataController {
private final WeatherDataService weatherDataService;
@AutoLog(value = "验证文件是否存在")
@Operation(summary = "验证文件是否存在")
@GetMapping("verifyFileExist")
public FileExistVo verifyFileExist(@NotBlank(message = "文件MD5值不能为空") String fileMD5Value) {
FileExistVo fileExist = weatherDataService.verifyFileExist(fileMD5Value);
return fileExist;
}
@AutoLog(value = "上传文件")
@Operation(summary = "上传文件")
@PostMapping("uploadFile")
public FileUploadResultVo uploadFile(FileVo fileVo){
if (!fileVo.getFileExt().equals(WeatherFileSuffixEnum.GRIB.getValue()) && !fileVo.getFileExt().equals(WeatherFileSuffixEnum.GRIB2.getValue())){
throw new RuntimeException("不支持当前上传的文件类型!");
}
FileUploadResultVo resultVo = weatherDataService.uploadFile(fileVo);
return resultVo;
}
/**
* 气象预测
* @return
*/
@AutoLog(value = "气象预测-气象数据查询")
@Operation(summary = "气象预测-气象数据查询")
@GetMapping(value = "getWeatherData")
public Result<WeatherResultVO> getWeatherData(Integer type,
LocalDateTime startTime,
int hour) {
return Result.OK(weatherDataService.getWeatherData(type,startTime,hour));
}
@AutoLog(value = "删除气象数据")
@Operation(summary = "删除气象数据")
@DeleteMapping("delete")
public Result<?> delete(@RequestBody List<String> ids){
weatherDataService.delete(ids);
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) {
try {
String md5 = calculateMD5("F:\\工作\\五木\\放射性核素监测数据综合分析及氙本底源解析系统\\其他资料\\气象数据\\中国CRA40再分析数据\\CRA40\\20250523\\CRA40_AVO_2025052300_GLB_0P25_HOUR_V1_0_0.grib2");
System.out.println("MD5: " + md5);
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,73 @@
package org.jeecg.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.query.PageRequest;
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.springframework.format.annotation.DateTimeFormat;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Validated
@RestController
@RequestMapping("weatherTask")
@RequiredArgsConstructor
public class WeatherTaskController {
private final WeatherTaskService weatherTaskService;
@AutoLog(value = "查询天气预测任务列表")
@Operation(summary = "查询天气预测任务列表")
@GetMapping(value = "page")
public Result<?> page(PageRequest pageRequest, String taskName, Integer taskStatus,
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate) {
IPage<WeatherTask> page = weatherTaskService.page(pageRequest, taskName, taskStatus, startDate,endDate);
Map<String, Object> rspData = new HashMap<>();
rspData.put("rows", page.getRecords());
rspData.put("total", page.getTotal());
return Result.OK(rspData);
}
@AutoLog(value = "新增天气预测任务")
@Operation(summary = "新增天气预测任务")
@PostMapping("create")
public Result<?> create(@RequestBody @Validated(value = InsertGroup.class) WeatherTask weatherTask){
weatherTaskService.cteate(weatherTask);
return Result.OK();
}
@AutoLog(value = "修改天气预测任务")
@Operation(summary = "修改天气预测任务")
@PutMapping("update")
public Result<?> update(@RequestBody @Validated(value = UpdateGroup.class) WeatherTask weatherTask){
weatherTaskService.update(weatherTask);
return Result.OK();
}
@AutoLog(value = "删除天气预测任务")
@Operation(summary = "删除天气预测任务")
@DeleteMapping("delete")
public Result<?> delete(@RequestBody List<String> ids){
weatherTaskService.delete(ids);
return Result.OK();
}
@AutoLog(value = "获取天气预测任务过程日志")
@Operation(summary = "获取天气预测任务过程日志")
@GetMapping("getTaskLog")
public Result<?> getTaskLog(@NotBlank(message = "预测任务ID不能为空") String taskId){
return Result.OK(weatherTaskService.getTaskLog(taskId));
}
}

View File

@ -0,0 +1,48 @@
package org.jeecg.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.jeecg.common.system.query.PageRequest;
import org.jeecg.modules.base.entity.WeatherData;
import org.jeecg.vo.FileExistVo;
import org.jeecg.vo.FileUploadResultVo;
import org.jeecg.vo.FileVo;
import org.jeecg.vo.WeatherResultVO;
import java.time.LocalDateTime;
import java.util.List;
public interface WeatherDataService {
WeatherResultVO getWeatherData(Integer type, LocalDateTime startTime, int hour);
/**
* 分页查询气象数据
* @param pageRequest
* @param fileExt
* @param dataSource
* @param startTime
* @param endTime
* @return
*/
IPage<WeatherData> page(PageRequest pageRequest,String fileExt,Integer dataSource,LocalDateTime startTime,LocalDateTime endTime);
/**
* 验证文件是否存在
* @param fileMD5Value 文件唯一MD5值
* @return
*/
FileExistVo verifyFileExist(String fileMD5Value);
/**
* 上传文件
* @param fileVo
*/
FileUploadResultVo uploadFile(FileVo fileVo);
/**
* 删除气象数据
* @param ids
*/
void delete(List<String> ids);
}

View File

@ -0,0 +1,58 @@
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.WeatherTask;
import org.jeecg.modules.base.entity.WeatherTaskLog;
import java.time.LocalDate;
import java.util.List;
/**
* 天气预报预测任务管理
*/
public interface WeatherTaskService extends IService<WeatherTask> {
/**
* 分页查询任务列表
* @param pageRequest
* @param taskName
* @param taskStatus
* @param startDate
* @param endDate
* @return
*/
IPage<WeatherTask> page(PageRequest pageRequest,String taskName,Integer taskStatus,LocalDate startDate,LocalDate endDate);
/**
* 创建天气预报预测任务
* @param weatherTask
*/
void cteate(WeatherTask weatherTask);
/**
* 修改天气预报预测任务
* @param weatherTask
*/
void update(WeatherTask weatherTask);
/**
* 删除预测任务
* @param ids
*/
void delete(List<String> ids);
/**
* 运行任务
* @param id
*/
void runTask(Integer id);
/**
* 获取任务运行日志
* @param taskId
* @return
*/
List<WeatherTaskLog> getTaskLog(String taskId);
}

View File

@ -0,0 +1,459 @@
package org.jeecg.service.impl;
import cn.hutool.core.io.FileUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.enums.WeatherDataSourceEnum;
import org.jeecg.common.constant.enums.WeatherTypeEnum;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.properties.SystemStorageProperties;
import org.jeecg.common.system.query.PageRequest;
import org.jeecg.common.util.NcUtil;
import org.jeecg.modules.base.entity.WeatherData;
import org.jeecg.modules.base.mapper.WeatherDataMapper;
import org.jeecg.service.WeatherDataService;
import org.jeecg.vo.FileExistVo;
import org.jeecg.vo.FileUploadResultVo;
import org.jeecg.vo.FileVo;
import org.jeecg.vo.WeatherResultVO;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import ucar.nc2.NetcdfFile;
import java.io.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@Slf4j
@Service
@RequiredArgsConstructor
public class WeatherDataServiceImpl extends ServiceImpl<WeatherDataMapper, WeatherData> implements WeatherDataService {
private final SystemStorageProperties systemStorageProperties;
/**
* 根据类型和小时数获取天气数据
* @param type 天气类型
* @param startTime 开始时间
* @param hour 小时数
* @return 天气数据列表
*/
public WeatherResultVO getWeatherData(Integer type, LocalDateTime startTime, int hour) {
// 参数校验
try {
Objects.requireNonNull(type, "天气类型不能为空");
Objects.requireNonNull(startTime, "开始时间不能为空");
if (hour <= 0) {
throw new IllegalArgumentException("小时数必须大于0");
}
} catch (NullPointerException | IllegalArgumentException e) {
log.error("参数校验失败: type={}, startTime={}, hour={}", type, startTime, hour, e);
throw e;
}
// 文件处理
String filePath;
try {
filePath = buildFilePath(startTime, hour, type);
if (!isFileValid(filePath)) {
log.warn("文件无效或不存在: {}", filePath);
return null;
}
} catch (Exception e) {
log.error("文件路径构建失败: type={}, hour={}", type, hour, e);
throw new JeecgBootException("文件路径处理失败", e);
}
WeatherResultVO weatherResultVO = new WeatherResultVO();
try (NetcdfFile ncFile = NetcdfFile.open(filePath)) {
// 基础数据获取
List<Double> lonData = NcUtil.getNCList(ncFile, "lon");
List<Double> latData = NcUtil.getNCList(ncFile, "lat");
if (lonData == null || lonData.isEmpty() || latData == null || latData.isEmpty()) {
log.error("经纬度数据为空: file={}", filePath);
throw new JeecgBootException("基础经纬度数据缺失");
}
// 按类型处理数据
List<List<Double>> dataList = new ArrayList<>();
try {
if (WeatherTypeEnum.WIND.getKey().equals(type)) {
getWindData(ncFile, weatherResultVO);
return weatherResultVO;
} else if (WeatherTypeEnum.TEMPERATURE.getKey().equals(type)) {
dataList = processTemperatureData(ncFile, lonData, latData);
} else if (WeatherTypeEnum.PRESSURE.getKey().equals(type)) {
dataList = processPressureData(ncFile, lonData, latData);
} else if (WeatherTypeEnum.HUMIDITY.getKey().equals(type)) {
dataList = processHumidityData(ncFile, lonData, latData);
} else {
throw new JeecgBootException("未知天气类型!");
}
} catch (Exception e) {
log.error("天气数据处理失败: type={}", type, e);
throw new JeecgBootException("天气数据处理异常", e);
}
// 结果处理
try {
List<Double> flattenedList = new ArrayList<>();
flattenList(dataList, flattenedList);
List<Double> maxMin = getMaxMin(flattenedList);
weatherResultVO.setMax(maxMin.get(0));
weatherResultVO.setMin(maxMin.get(1));
weatherResultVO.setSn(latData.size());
weatherResultVO.setWe(lonData.size());
weatherResultVO.setDataList(dataList);
} catch (Exception e) {
log.error("结果数据处理失败", e);
throw new JeecgBootException("结果数据处理异常", e);
}
} catch (IOException e) {
log.error("NetCDF文件处理失败: {}", filePath, e);
throw new JeecgBootException("文件读取失败", e);
} catch (Exception e) {
log.error("未知处理错误", e);
throw new JeecgBootException("未知处理错误", e);
}
return weatherResultVO;
}
/**
* 分页查询气象数据
*
* @param pageRequest
* @param fileExt
* @param dataSource
* @param startTime
* @param endTime
* @return
*/
@Override
public IPage<WeatherData> page(PageRequest pageRequest, String fileExt, Integer dataSource, LocalDateTime startTime, LocalDateTime endTime) {
LambdaQueryWrapper<WeatherData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Objects.nonNull(dataSource),WeatherData::getDataSource, dataSource);
queryWrapper.between((Objects.nonNull(startTime) && Objects.nonNull(endTime)),WeatherData::getDataStartTime,startTime,endTime);
queryWrapper.eq(StringUtils.isNotBlank(fileExt),WeatherData::getFileExt, fileExt);
IPage<WeatherData> iPage = new Page<>(pageRequest.getPageNum(),pageRequest.getPageSize());
return this.baseMapper.selectPage(iPage, queryWrapper);
}
/**
* 验证文件是否存在
*
* @param fileMD5Value 文件唯一MD5值
* @return
*/
@Override
public FileExistVo verifyFileExist(String fileMD5Value) {
LambdaQueryWrapper<WeatherData> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(WeatherData::getMd5Value, fileMD5Value);
FileExistVo fileExist = new FileExistVo();
WeatherData weatherData = this.baseMapper.selectOne(wrapper);
if(Objects.nonNull(weatherData)) {
fileExist.setExist(true);
fileExist.setFileId(weatherData.getId());
fileExist.setShareTotal(weatherData.getShareTotal());
fileExist.setShareIndex(weatherData.getShareIndex());
}
return fileExist;
}
/**
* 上传文件
*
* @param fileVo
*/
@Transactional(rollbackFor = RuntimeException.class)
@Override
public FileUploadResultVo uploadFile(FileVo fileVo) {
try{
MultipartFile file = fileVo.getFile();
//文件保存路径
String storagePath = this.getFileStoragePath(fileVo.getDataSource(), fileVo.getFileName());
fileVo.setFilePath(storagePath);
//如果上传策略是分片则加分片后缀
if(fileVo.isFileShare()) {
storagePath += StringPool.DOT;
storagePath += fileVo.getShareIndex();
}
//保存文件
File shareFile = new File(storagePath.toString());
if(!shareFile.getParentFile().exists()) {
shareFile.getParentFile().setWritable(true);
shareFile.getParentFile().mkdirs();
}
file.transferTo(shareFile);
//保存文件信息入库
String id= null;
FileExistVo fileExist = this.verifyFileExist(fileVo.getMd5Value());
if(fileExist.isExist()) {
WeatherData queryResult = this.baseMapper.selectById(fileExist.getFileId());
queryResult.setShareIndex(fileVo.getShareIndex());
this.baseMapper.updateById(queryResult);
id = queryResult.getId();
}else {
WeatherData weatherData = new WeatherData();
BeanUtils.copyProperties(fileVo, weatherData);
this.baseMapper.insert(weatherData);
id = weatherData.getId();
}
boolean flag = false;
if(fileVo.isFileShare()) {
if(fileVo.getShareIndex() == (fileVo.getShareTotal()-1)) {
this.merge(fileVo);
flag = true;
}
}else {
flag = true;
}
//获取文件数据开始日期
//计算文件大小M
WeatherData queryResult = this.baseMapper.selectById(id);
File dataFile = new File(storagePath);
if(dataFile.exists() && dataFile.length()>0){
BigDecimal divideVal = new BigDecimal("1024");
BigDecimal bg = new BigDecimal(dataFile.length());
BigDecimal fileSize = bg.divide(divideVal).divide(divideVal).setScale(2, RoundingMode.HALF_UP);
queryResult.setFileSize(fileSize.doubleValue());
this.baseMapper.updateById(queryResult);
}
FileUploadResultVo result = new FileUploadResultVo();
result.setCompleted(flag);
result.setId(id);
return result;
}catch (Exception e){
throw new RuntimeException(e);
}
}
/**
* 删除气象数据
*
* @param ids
*/
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void delete(List<String> ids) {
this.baseMapper.deleteBatchIds(ids);
}
/**
* 文件合并
* @param fileVo
* @throws Exception
*/
private void merge(FileVo fileVo)throws Exception {
String storagePath = this.getFileStoragePath(fileVo.getDataSource(), fileVo.getFileName());
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(storagePath,true));
BufferedInputStream bis = null;
try {
byte[] byt = new byte[10*1024];
int len;
for(int i=0;i<fileVo.getShareTotal();i++) {
bis = new BufferedInputStream(new FileInputStream(storagePath+"."+i));
while((len = bis.read(byt))!=-1) {
bos.write(byt, 0, len);
}
if(null != bis) {
bis.close();
}
}
} catch (Exception e) {
log.error(fileVo.getFileName()+"文件分片上传异常");
throw new RuntimeException("文件上传失败,请重新上传");
}finally {
if(null != bos) {
bos.flush();
bos.close();
}
if(null != bis) {
bis.close();
}
}
for(int i=0;i<fileVo.getShareTotal();i++) {
File file = new File(storagePath+"."+i);
if(file.exists()) {
file.delete();
}
}
}
/**
* 拼接文件存储路径
* @param dataSource
* @param fileName
* @return
*/
private String getFileStoragePath(Integer dataSource,String fileName) {
StringBuilder storagePath = new StringBuilder();
storagePath.append(this.systemStorageProperties.getRootPath());
storagePath.append(File.separator);
if (WeatherDataSourceEnum.PANGU.getKey().equals(dataSource)) {
storagePath.append(this.systemStorageProperties.getPangu());
} else if (WeatherDataSourceEnum.GRAPHCAST.getKey().equals(dataSource)) {
storagePath.append(this.systemStorageProperties.getGraphcast());
}else if (WeatherDataSourceEnum.RE_ANALYSIS.getKey().equals(dataSource)) {
storagePath.append(this.systemStorageProperties.getReAnalysis());
}
storagePath.append(File.separator);
storagePath.append(fileName);
return storagePath.toString();
}
/**
* 构建文件路径
*/
private String buildFilePath(LocalDateTime startTime, int hour, Integer type) {
// LocalDateTime targetTime = startTime.plusHours(hour);
// String fileName = WeatherFilePrefixEnum.getValueByKey(type) + targetTime.format(DateTimeFormatter.ofPattern("yyyyMMddHH"))
// + WeatherFileSuffixEnum.getValueByKey(type);
// return systemStorageProperties.getWeather_file() + fileName;
return null;
}
/**
* 检查文件是否有效
*/
private boolean isFileValid(String filePath) {
Path path = Paths.get(filePath);
return Files.exists(path) && !Files.isDirectory(path);
}
/**
* 获取风场数据
*/
private void getWindData(NetcdfFile ncFile,WeatherResultVO weatherResultVO) {
List<List<Double>> results = new ArrayList<>();
try {
List<List<Double>> u = NcUtil.getNCByName(ncFile, "u-component_of_wind_height_above_ground", 0).get(0);
List<List<Double>> v = NcUtil.getNCByName(ncFile, "v-component_of_wind_height_above_ground", 0).get(0);
for (int i = 0; i < u.size(); i++) {
for (int j = 0; j < u.get(0).size(); j++) {
results.add(List.of(u.get(i).get(j), v.get(i).get(j)));
}
}
weatherResultVO.setSn(u.size());
weatherResultVO.setWe(u.get(0).size());
weatherResultVO.setDataList(results);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 处理温度数据
*/
private List<List<Double>> processTemperatureData(NetcdfFile ncFile, List<Double> lonData, List<Double> latData) {
try {
List<List<List<Double>>> tDataList = NcUtil.getNCByName(ncFile, "Temperature_height_above_ground", 0);
return processWeatherData(tDataList, lonData, latData, value -> value - 273.15);
} catch (Exception e) {
return Collections.emptyList();
}
}
/**
* 处理气压数据
*/
private List<List<Double>> processPressureData(NetcdfFile ncFile, List<Double> lonData, List<Double> latData) {
try {
List<List<List<Double>>> pDataList = NcUtil.getNCByName(ncFile, "Pressure_msl", 0);
return processWeatherData(pDataList, lonData, latData, value -> value / 1000);
} catch (Exception e) {
return Collections.emptyList();
}
}
/**
* 处理湿度数据
*/
private List<List<Double>> processHumidityData(NetcdfFile ncFile, List<Double> lonData, List<Double> latData) {
try {
List<List<List<Double>>> hDataList = NcUtil.getNCByName(ncFile, "Specific_humidity_isobaric", 0);
return processWeatherData(hDataList, lonData, latData, value -> value);
} catch (Exception e) {
return Collections.emptyList();
}
}
/**
* 通用天气数据处理方法
*/
private List<List<Double>> processWeatherData(List<List<List<Double>>> dataList,
List<Double> lonData,
List<Double> latData,
ValueConverter converter) {
List<List<Double>> results = new ArrayList<>();
for (int i = 0; i < latData.size(); i++) {
for (int j = 0; j < lonData.size(); j++) {
double value = j < dataList.get(0).get(i).size()
? dataList.get(0).get(i).get(j)
: dataList.get(0).get(i).get(0);
results.add(List.of(lonData.get(j), latData.get(i), converter.convert(value)));
}
}
return results;
}
// 递归地将嵌套列表中的所有 Double 值提取出来
public void flattenList(List<?> nestedList, List<Double> flattenedList) {
for (Object element : nestedList) {
if (element instanceof List) {
flattenList((List<?>) element, flattenedList);
} else {
flattenedList.add((Double) element);
}
}
}
/**
* 获取最大最小值
*/
public static List<Double> getMaxMin(List<Double> flattenedList) {
if (flattenedList == null || flattenedList.isEmpty()) {
return List.of(0.0, 0.0);
}
return List.of(Collections.max(flattenedList), Collections.min(flattenedList));
}
/**
* 函数式接口用于值转换
*/
@FunctionalInterface
private interface ValueConverter {
double convert(double value);
}
}

View File

@ -0,0 +1,151 @@
package org.jeecg.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.enums.WeatherTaskStatusEnum;
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.WeatherTaskLogMapper;
import org.jeecg.modules.base.mapper.WeatherTaskMapper;
import org.jeecg.service.WeatherTaskService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
/**
* 天气预报预测任务管理
*/
@Service
@RequiredArgsConstructor
public class WeatherTaskServiceImpl extends ServiceImpl<WeatherTaskMapper, WeatherTask> implements WeatherTaskService {
private final WeatherTaskLogMapper weatherTaskLogMapper;
/**
* 分页查询任务列表
* @param pageRequest
* @param taskName
* @param taskStatus
* @param startDate
* @param endDate
* @return
*/
@Override
public IPage<WeatherTask> page(PageRequest pageRequest, String taskName, Integer taskStatus, LocalDate startDate,LocalDate endDate) {
LambdaQueryWrapper<WeatherTask> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Objects.nonNull(taskStatus),WeatherTask::getTaskStatus, taskStatus);
queryWrapper.between((Objects.nonNull(startDate) && Objects.nonNull(endDate)),WeatherTask::getStartDate,startDate,endDate);
queryWrapper.like(StringUtils.isNotBlank(taskName),WeatherTask::getTaskName,taskName);
queryWrapper.orderByDesc(WeatherTask::getStartDate);
IPage<WeatherTask> iPage = new Page<>(pageRequest.getPageNum(), pageRequest.getPageSize());
return this.page(iPage, queryWrapper);
}
/**
* 创建天气预报预测任务
* @param weatherTask
*/
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void cteate(WeatherTask weatherTask) {
//校验任务名称是否已存在
LambdaQueryWrapper<WeatherTask> checkNameWrapper = new LambdaQueryWrapper<>();
checkNameWrapper.eq(WeatherTask::getTaskName,weatherTask.getTaskName());
WeatherTask checkNameResult = this.getOne(checkNameWrapper);
if(Objects.nonNull(checkNameResult)){
throw new RuntimeException("此任务名称已存在");
}
//校验任务预测时间是否已重复
LambdaQueryWrapper<WeatherTask> checkDateWrapper = new LambdaQueryWrapper<>();
checkDateWrapper.eq(WeatherTask::getStartDate,weatherTask.getStartDate());
checkDateWrapper.eq(WeatherTask::getStartTime,weatherTask.getStartTime());
WeatherTask checkDateResult = this.getOne(checkDateWrapper);
if(Objects.nonNull(checkDateResult)){
throw new RuntimeException("此当前预测时间为参数的任务已存在");
}
weatherTask.setTaskStatus(WeatherTaskStatusEnum.NOT_STARTED.getKey());
this.save(weatherTask);
}
/**
* 修改天气预报预测任务
* @param weatherTask
*/
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void update(WeatherTask weatherTask) {
//校验数据是否存在
WeatherTask queryResult = this.getById(weatherTask.getId());
if(Objects.isNull(queryResult)){
throw new RuntimeException("此任务不存在");
}
//校验任务名称是否已存在
LambdaQueryWrapper<WeatherTask> checkNameWrapper = new LambdaQueryWrapper<>();
checkNameWrapper.eq(WeatherTask::getTaskName,weatherTask.getTaskName());
WeatherTask checkNameResult = this.getOne(checkNameWrapper);
if(Objects.nonNull(checkNameResult) && !weatherTask.getId().equals(checkNameResult.getId())){
throw new RuntimeException("此任务名称已存在");
}
//校验任务预测时间是否已重复
LambdaQueryWrapper<WeatherTask> checkDateWrapper = new LambdaQueryWrapper<>();
checkDateWrapper.eq(WeatherTask::getStartDate,weatherTask.getStartDate());
checkDateWrapper.eq(WeatherTask::getStartTime,weatherTask.getStartTime());
WeatherTask checkDateResult = this.getOne(checkDateWrapper);
if(Objects.nonNull(checkDateResult) && !weatherTask.getId().equals(checkNameResult.getId())){
throw new RuntimeException("此当前预测时间为参数的任务已存在");
}
queryResult.setTaskName(weatherTask.getTaskName());
queryResult.setPredictionModel(weatherTask.getPredictionModel());
queryResult.setStartDate(weatherTask.getStartDate());
queryResult.setStartTime(weatherTask.getStartTime());
queryResult.setLeadTime(weatherTask.getLeadTime());
queryResult.setDataSources(weatherTask.getDataSources());
queryResult.setInputFile(weatherTask.getInputFile());
this.updateById(queryResult);
}
/**
* 删除预测任务
* @param ids
*/
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void delete(List<String> ids) {
this.baseMapper.deleteBatchIds(ids);
}
/**
* 运行任务
*
* @param id
*/
@Override
public void runTask(Integer id) {
}
/**
* 获取任务运行日志
*
* @param taskId
* @return
*/
@Override
public List<WeatherTaskLog> getTaskLog(String taskId) {
LambdaQueryWrapper<WeatherTaskLog> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(WeatherTaskLog::getTaskId,taskId);
queryWrapper.select(WeatherTaskLog::getCreateTime,WeatherTaskLog::getLogContent);
queryWrapper.orderByAsc(WeatherTaskLog::getCreateTime);
return this.weatherTaskLogMapper.selectList(queryWrapper);
}
}

View File

@ -0,0 +1,23 @@
package org.jeecg.vo;
import lombok.Data;
import java.io.Serializable;
/**
* 校验文件是否存在VO
*/
@Data
public class FileExistVo implements Serializable{
private static final long serialVersionUID = 1L;
private boolean exist;
private String fileId;
private Integer shareTotal;
private Integer shareIndex;
}

View File

@ -0,0 +1,24 @@
package org.jeecg.vo;
import lombok.Data;
import java.io.Serializable;
/**
* 上传文件结果VO
*/
@Data
public class FileUploadResultVo implements Serializable{
private static final long serialVersionUID = 1L;
/**
* 是否上传完毕
*/
private boolean completed;
/**
* 记录id
*/
private String id;
}

View File

@ -0,0 +1,28 @@
package org.jeecg.vo;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.modules.base.entity.WeatherData;
import org.springframework.web.multipart.MultipartFile;
/**
* 上传文件VO
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class FileVo extends WeatherData {
private static final long serialVersionUID = 1L;
/**
* 是否进行分片
*/
private boolean fileShare;
/**
* 上传文件
*/
@JSONField(serialize = false)
private MultipartFile file;
}

View File

@ -0,0 +1,14 @@
package org.jeecg.vo;
import lombok.Data;
import java.util.List;
@Data
public class WeatherResultVO {
private List<List<Double>> dataList;
private double max;
private double min;
private int sn;
private int we;
}

View File

@ -28,13 +28,7 @@
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-system-biz</artifactId>
<!-- 排除demo模块demo模块采用微服务独立启动 -->
<exclusions>
<exclusion>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-module-demo</artifactId>
</exclusion>
</exclusions>
<version>${jeecgboot.version}</version>
</dependency>
<!-- feign 熔断限流、分布式锁、xxljob示例

View File

@ -17,16 +17,10 @@ import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* 微服务启动类采用此类启动项目为微服务模式
* 注意 需要先初始化Nacos的数据库脚本db/tables_nacos.sql
* @author zyf
* @date: 2022/4/21 10:55
*/
@Slf4j
@SpringBootApplication
@EnableFeignClients(basePackages = {"org.jeecg"})
@ -48,7 +42,7 @@ public class JeecgSystemCloudApplication extends SpringBootServletInitializer im
String port = env.getProperty("server.port");
String path = oConvertUtils.getString(env.getProperty("server.servlet.context-path"));
log.info("\n----------------------------------------------------------\n\t" +
"Application Jeecg-Boot is running! Access URLs:\n\t" +
"Application STAS-System is running! Access URLs:\n\t" +
"Local: \t\thttp://localhost:" + port + path + "/doc.html\n" +
"External: \thttp://" + ip + ":" + port + path + "/doc.html\n" +
"Swagger文档: \thttp://" + ip + ":" + port + path + "/doc.html\n" +

View File

@ -1,5 +1,5 @@
server:
port: 7001
port: 8001
spring:
application:

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-server-cloud</artifactId>
<version>3.8.1</version>
</parent>
<artifactId>jeecg-weather-start</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-starter3-cloud</artifactId>
</dependency>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-system-cloud-api</artifactId>
<version>${jeecgboot.version}</version>
</dependency>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-module-weather</artifactId>
<version>${jeecgboot.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,55 @@
package org.jeecg;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.base.BaseMap;
import org.jeecg.common.constant.GlobalConstants;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Slf4j
@SpringBootApplication
@RequiredArgsConstructor
@EnableFeignClients
public class JeecgWeatherCloudApplication extends SpringBootServletInitializer implements CommandLineRunner {
private final RedisTemplate<String, Object> redisTemplate;
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(JeecgWeatherCloudApplication.class);
}
public static void main(String[] args) throws UnknownHostException {
ConfigurableApplicationContext application = SpringApplication.run(JeecgWeatherCloudApplication.class, args);
Environment env = application.getEnvironment();
String ip = InetAddress.getLocalHost().getHostAddress();
String port = env.getProperty("server.port");
String path = oConvertUtils.getString(env.getProperty("server.servlet.context-path"));
log.info("\n----------------------------------------------------------\n\t" +
"Application STAS-Weather is running! Access URLs:\n\t" +
"Local: \t\thttp://localhost:" + port + path + "/doc.html\n" +
"External: \thttp://" + ip + ":" + port + path + "/doc.html\n" +
"Swagger文档: \thttp://" + ip + ":" + port + path + "/doc.html\n" +
"----------------------------------------------------------");
}
@Override
public void run(String... args) {
BaseMap params = new BaseMap();
params.put(GlobalConstants.HANDLER_NAME, GlobalConstants.LODER_ROUDER_HANDLER);
//刷新网关
redisTemplate.convertAndSend(GlobalConstants.REDIS_TOPIC_NAME, params);
}
}

View File

@ -0,0 +1,24 @@
server:
port: 8002
spring:
application:
name: jeecg-weather
cloud:
nacos:
config:
server-addr: @config.server-addr@
group: @config.group@
namespace: @config.namespace@
username: @config.username@
password: @config.password@
discovery:
server-addr: ${spring.cloud.nacos.config.server-addr}
group: @config.group@
namespace: @config.namespace@
username: @config.username@
password: @config.password@
config:
import:
- optional:nacos:jeecg.yaml
- optional:nacos:jeecg-@profile.name@.yaml

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 -->
<property name="LOG_HOME" value="../logs" />
<!--<property name="COLOR_PATTERN" value="%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta( %replace(%caller{1}){'\t|Caller.{1}0|\r\n', ''})- %gray(%msg%xEx%n)" />-->
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${LOG_HOME}/jeecg-system-%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
</encoder>
</appender>
<!-- 生成 error html格式日志开始 -->
<appender name="HTML" class="ch.qos.logback.core.FileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!--设置日志级别,过滤掉info日志,只输入error日志-->
<level>ERROR</level>
</filter>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>%p%d%msg%M%F{32}%L</pattern>
</layout>
</encoder>
<file>${LOG_HOME}/error-log.html</file>
</appender>
<!-- 生成 error html格式日志结束 -->
<!-- 每天生成一个html格式的日志开始 -->
<appender name="FILE_HTML" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${LOG_HOME}/jeecg-system-%d{yyyy-MM-dd}.%i.html</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
<MaxFileSize>10MB</MaxFileSize>
</rollingPolicy>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>%p%d%msg%M%F{32}%L</pattern>
</layout>
</encoder>
</appender>
<!-- 每天生成一个html格式的日志结束 -->
<!--myibatis log configure -->
<logger name="com.apache.ibatis" level="TRACE" />
<logger name="java.sql.Connection" level="DEBUG" />
<logger name="java.sql.Statement" level="DEBUG" />
<logger name="java.sql.PreparedStatement" level="DEBUG" />
<!-- 日志输出级别 -->
<root level="INFO">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
<appender-ref ref="HTML" />
<appender-ref ref="FILE_HTML" />
</root>
</configuration>

View File

@ -20,6 +20,7 @@
<!-- 监控和测试例子 -->
<module>jeecg-visual</module>
<module>jeecg-weather-start</module>
</modules>
</project>

View File

@ -84,6 +84,7 @@
<modules>
<module>jeecg-boot-base-core</module>
<module>jeecg-module-system</module>
<module>jeecg-module-weather</module>
</modules>
<repositories>