diff --git a/pom.xml b/pom.xml index 766d675..303ae36 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,7 @@ 1.82 2.0.60 3.5.9 + 1.12.2 @@ -120,6 +121,13 @@ spring-boot-starter-aop + + + com.deepoove + poi-tl + ${poi-tl-version} + + org.springframework.boot diff --git a/src/main/java/com/gis/xian/config/AlgorithmClient.java b/src/main/java/com/gis/xian/config/AlgorithmClient.java new file mode 100644 index 0000000..5a597ca --- /dev/null +++ b/src/main/java/com/gis/xian/config/AlgorithmClient.java @@ -0,0 +1,182 @@ +package com.gis.xian.config; + +import com.alibaba.fastjson2.JSON; +import com.gis.xian.domain.ApiResponse; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.StreamUtils; +import org.springframework.web.client.RestClient; + +import java.util.Collections; +import java.util.Enumeration; +import java.util.Map; + +/** + * 算法服务 HTTP 客户端 + * + * 两种使用方式: + * 1. Controller 透传前端请求:{@link #proxyRequest(HttpServletRequest, HttpMethod)} + * 2. Service 直接调用:{@link #get(String, Class)} / {@link #post(String, Object, Class)} + */ +@Slf4j +@Component +public class AlgorithmClient { + + @Resource + private AlgorithmServerProperties props; + + private RestClient restClient; + + @PostConstruct + public void init() { + SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); + factory.setConnectTimeout(props.getConnectTimeout() * 1000); + factory.setReadTimeout(props.getReadTimeout() * 1000); + this.restClient = RestClient.builder().requestFactory(factory).build(); + log.info("AlgorithmClient 初始化: url={}, connectTimeout={}s, readTimeout={}s", + props.getUrl(), props.getConnectTimeout(), props.getReadTimeout()); + } + + // ================================================================ + // 前端透传:完整代理 HTTP 请求 + // ================================================================ + + public ApiResponse proxyGet(HttpServletRequest request) { + return proxyRequest(request, HttpMethod.GET); + } + + public ApiResponse proxyPost(HttpServletRequest request) { + return proxyRequest(request, HttpMethod.POST); + } + + public ApiResponse proxyPut(HttpServletRequest request) { + return proxyRequest(request, HttpMethod.PUT); + } + + public ApiResponse proxyDelete(HttpServletRequest request) { + return proxyRequest(request, HttpMethod.DELETE); + } + + public ApiResponse proxyPatch(HttpServletRequest request) { + return proxyRequest(request, HttpMethod.PATCH); + } + + @SuppressWarnings("unchecked") + private ApiResponse proxyRequest(HttpServletRequest request, HttpMethod httpMethod) { + long startTime = System.currentTimeMillis(); + try { + String targetUrl = buildTargetUrl(request); + log.info("代理请求: {} -> {}", request.getRequestURI(), targetUrl); + + RestClient.RequestBodySpec spec = restClient + .method(httpMethod) + .uri(targetUrl) + .headers(h -> copyRequestHeaders(request, h)); + + if (httpMethod == HttpMethod.POST || httpMethod == HttpMethod.PUT || httpMethod == HttpMethod.PATCH) { + String contentType = request.getContentType(); + byte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream()); + if (bodyBytes.length > 0) { + if (contentType != null) { + spec.contentType(MediaType.parseMediaType(contentType)); + } + spec.body(bodyBytes); + } + } + + ResponseEntity response = spec.retrieve().toEntity(String.class); + long elapsed = System.currentTimeMillis() - startTime; + log.info("代理完成: {} -> {}, {}ms, HTTP {}", request.getRequestURI(), targetUrl, elapsed, response.getStatusCode().value()); + + return parseResponseBody(response.getBody()); + + } catch (Exception e) { + long elapsed = System.currentTimeMillis() - startTime; + log.error("代理失败: {}, {}ms, {}", request.getRequestURI(), elapsed, e.getMessage(), e); + return ApiResponse.error(502, "算法服务调用失败: " + e.getMessage(), null); + } + } + + // ================================================================ + // 后端直接调用(类型安全) + // ================================================================ + + public T get(String path, Class responseType) { + String url = props.getUrl() + path; + log.info("GET {}", url); + return restClient.get().uri(url).retrieve().body(responseType); + } + + public T get(String path, ParameterizedTypeReference responseType) { + String url = props.getUrl() + path; + log.info("GET {}", url); + return restClient.get().uri(url).retrieve().body(responseType); + } + + public T post(String path, Object body, Class responseType) { + String url = props.getUrl() + path; + log.info("POST {} body={}", url, body); + return restClient.post().uri(url).body(body).retrieve().body(responseType); + } + + public T post(String path, Object body, ParameterizedTypeReference responseType) { + String url = props.getUrl() + path; + log.info("POST {} body={}", url, body); + return restClient.post().uri(url).body(body).retrieve().body(responseType); + } + + // ================================================================ + // 内部工具方法 + // ================================================================ + + private String buildTargetUrl(HttpServletRequest request) { + String path = request.getRequestURI().substring("/algorithm-api".length()); + if (!path.startsWith("/")) { + path = "/" + path; + } + String baseUrl = props.getUrl(); + if (baseUrl.endsWith("/") && path.startsWith("/")) { + baseUrl = baseUrl.substring(0, baseUrl.length() - 1); + } + String queryString = request.getQueryString(); + return queryString != null ? baseUrl + path + "?" + queryString : baseUrl + path; + } + + private void copyRequestHeaders(HttpServletRequest request, HttpHeaders headers) { + Enumeration names = request.getHeaderNames(); + while (names.hasMoreElements()) { + String name = names.nextElement(); + if (shouldExcludeHeader(name)) continue; + headers.addAll(name, Collections.list(request.getHeaders(name))); + } + } + + private boolean shouldExcludeHeader(String name) { + String lower = name.toLowerCase(); + return lower.equals("host") || lower.equals("content-length") || lower.equals("transfer-encoding"); + } + + @SuppressWarnings("unchecked") + private ApiResponse parseResponseBody(String body) { + if (body == null || body.isEmpty()) { + return ApiResponse.ok(null); + } + try { + Map map = JSON.parseObject(body, Map.class); + if (map.containsKey("code") && map.containsKey("message")) { + return new ApiResponse<>((Integer) map.get("code"), (String) map.get("message"), map.get("data")); + } + } catch (Exception ignored) { + } + return ApiResponse.ok((Object) body); + } +} diff --git a/src/main/java/com/gis/xian/config/AlgorithmServerProperties.java b/src/main/java/com/gis/xian/config/AlgorithmServerProperties.java index 10cdbf8..94c5f5e 100644 --- a/src/main/java/com/gis/xian/config/AlgorithmServerProperties.java +++ b/src/main/java/com/gis/xian/config/AlgorithmServerProperties.java @@ -16,4 +16,14 @@ public class AlgorithmServerProperties { * 算法服务器地址 */ private String url; + + /** + * 连接超时(秒) + */ + private int connectTimeout; + + /** + * 读取超时(秒) + */ + private int readTimeout; } diff --git a/src/main/java/com/gis/xian/config/ReportProperties.java b/src/main/java/com/gis/xian/config/ReportProperties.java new file mode 100644 index 0000000..e851b01 --- /dev/null +++ b/src/main/java/com/gis/xian/config/ReportProperties.java @@ -0,0 +1,26 @@ +package com.gis.xian.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Data +@Component +@ConfigurationProperties(prefix = "report") +public class ReportProperties { + private DisasterCausingFactors disasterCausingFactors; + + @Data + public static class DisasterCausingFactors { + private FactorConfig rainfall; + private FactorConfig earthquake; + } + + @Data + public static class FactorConfig { + private List type; + private Integer number; + } +} diff --git a/src/main/java/com/gis/xian/controller/AlgorithmProxyController.java b/src/main/java/com/gis/xian/controller/AlgorithmProxyController.java index 9d405d9..368c8db 100644 --- a/src/main/java/com/gis/xian/controller/AlgorithmProxyController.java +++ b/src/main/java/com/gis/xian/controller/AlgorithmProxyController.java @@ -1,24 +1,14 @@ package com.gis.xian.controller; -import com.alibaba.fastjson2.JSON; -import com.gis.xian.config.AlgorithmServerProperties; import com.gis.xian.domain.ApiResponse; +import com.gis.xian.config.AlgorithmClient; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; -import org.springframework.http.*; -import org.springframework.util.StreamUtils; import org.springframework.web.bind.annotation.*; -import org.springframework.web.client.RestTemplate; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Enumeration; -import java.util.Map; /** - * 算法代理控制器 - * 所有以 /algorithm-api 开头的请求都会转发到算法服务器 + * 算法代理控制器 — 前端请求透传到算法服务器 */ @RestController @RequestMapping("/algorithm-api") @@ -26,197 +16,30 @@ import java.util.Map; public class AlgorithmProxyController extends BaseController { @Resource - private AlgorithmServerProperties algorithmServerProperties; + private AlgorithmClient algorithmClient; - private final RestTemplate restTemplate = new RestTemplate(); - - /** - * 处理所有 GET 请求 - */ @GetMapping("/**") public ApiResponse proxyGet(HttpServletRequest request) { - return proxyRequest(request, HttpMethod.GET); + return algorithmClient.proxyGet(request); } - /** - * 处理所有 POST 请求 - */ @PostMapping("/**") - public ApiResponse proxyPost(HttpServletRequest request) throws IOException { - return proxyRequest(request, HttpMethod.POST); + public ApiResponse proxyPost(HttpServletRequest request) { + return algorithmClient.proxyPost(request); } - /** - * 处理所有 PUT 请求 - */ @PutMapping("/**") - public ApiResponse proxyPut(HttpServletRequest request) throws IOException { - return proxyRequest(request, HttpMethod.PUT); + public ApiResponse proxyPut(HttpServletRequest request) { + return algorithmClient.proxyPut(request); } - /** - * 处理所有 DELETE 请求 - */ @DeleteMapping("/**") public ApiResponse proxyDelete(HttpServletRequest request) { - return proxyRequest(request, HttpMethod.DELETE); + return algorithmClient.proxyDelete(request); } - /** - * 处理所有 PATCH 请求 - */ @PatchMapping("/**") - public ApiResponse proxyPatch(HttpServletRequest request) throws IOException { - return proxyRequest(request, HttpMethod.PATCH); - } - - /** - * 通用的请求代理方法 - */ - @SuppressWarnings("unchecked") - private ApiResponse proxyRequest(HttpServletRequest request, HttpMethod httpMethod) { - long startTime = System.currentTimeMillis(); - - try { - // 构建目标 URL - String targetUrl = buildTargetUrl(request); - log.info("代理请求: {} -> {}", request.getRequestURI(), targetUrl); - - // 构建请求头 - HttpHeaders headers = buildRequestHeaders(request); - - // 构建请求体 - Object requestBody = null; - if (httpMethod == HttpMethod.POST || httpMethod == HttpMethod.PUT || httpMethod == HttpMethod.PATCH) { - String contentType = request.getContentType(); - if (contentType != null && !contentType.isEmpty()) { - headers.setContentType(MediaType.parseMediaType(contentType)); - - // 读取请求体 - byte[] bodyBytes = StreamUtils.copyToByteArray(request.getInputStream()); - if (bodyBytes.length > 0) { - // 尝试解析为 JSON,如果不是 JSON 则直接使用字节数组 - if (contentType.contains("application/json")) { - String jsonBody = new String(bodyBytes, StandardCharsets.UTF_8); - try { - requestBody = JSON.parse(jsonBody); - } catch (Exception e) { - requestBody = jsonBody; - } - } else { - requestBody = bodyBytes; - } - } - } - } - - // 创建 HTTP 实体 - HttpEntity entity = new HttpEntity<>(requestBody, headers); - - // 发送请求到算法服务器 - ResponseEntity response = restTemplate.exchange( - targetUrl, - httpMethod, - entity, - String.class - ); - - // 解析响应 - long endTime = System.currentTimeMillis(); - log.info("代理请求完成: {} -> {}, 耗时: {}ms, 状态码: {}", - request.getRequestURI(), targetUrl, (endTime - startTime), response.getStatusCode()); - - // 尝试将响应解析为 ApiResponse - String responseBody = response.getBody(); - if (responseBody != null && !responseBody.isEmpty()) { - try { - // 尝试解析为 ApiResponse 格式 - Map responseMap = JSON.parseObject(responseBody, Map.class); - if (responseMap.containsKey("code") && responseMap.containsKey("message")) { - // 如果已经是 ApiResponse 格式,直接返回 - Integer code = (Integer) responseMap.get("code"); - String message = (String) responseMap.get("message"); - Object data = responseMap.get("data"); - return new ApiResponse<>(code, message, data); - } - } catch (Exception e) { - log.debug("响应不是标准 ApiResponse 格式,将作为数据返回"); - } - - // 如果不是 ApiResponse 格式,将整个响应作为 data 返回 - return ApiResponse.ok((Object) responseBody); - } - - return ApiResponse.ok((Object) null); - - } catch (Exception e) { - long endTime = System.currentTimeMillis(); - log.error("代理请求失败: {} , 耗时: {}ms, 错误: {}", - request.getRequestURI(), (endTime - startTime), e.getMessage(), e); - return ApiResponse.error(502, "算法服务调用失败: " + e.getMessage(), null); - } - } - - /** - * 构建目标 URL - */ - private String buildTargetUrl(HttpServletRequest request) { - // 获取原始请求路径,去掉 /algorithm-api 前缀 - String requestUri = request.getRequestURI(); - String path = requestUri.substring("/algorithm-api".length()); - - // 确保路径以 / 开头 - if (!path.startsWith("/")) { - path = "/" + path; - } - - // 拼接算法服务器地址和路径 - String baseUrl = algorithmServerProperties.getUrl(); - if (baseUrl.endsWith("/") && path.startsWith("/")) { - baseUrl = baseUrl.substring(0, baseUrl.length() - 1); - } - - String targetUrl = baseUrl + path; - - // 添加查询参数 - String queryString = request.getQueryString(); - if (queryString != null && !queryString.isEmpty()) { - targetUrl += "?" + queryString; - } - - return targetUrl; - } - - /** - * 构建请求头 - */ - private HttpHeaders buildRequestHeaders(HttpServletRequest request) { - HttpHeaders headers = new HttpHeaders(); - - // 复制所有请求头(排除一些特定的头) - Enumeration headerNames = request.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = headerNames.nextElement(); - // 排除一些不应该转发的头 - if (!shouldExcludeHeader(headerName)) { - Enumeration headerValues = request.getHeaders(headerName); - while (headerValues.hasMoreElements()) { - headers.add(headerName, headerValues.nextElement()); - } - } - } - - return headers; - } - - /** - * 判断是否应该排除该请求头 - */ - private boolean shouldExcludeHeader(String headerName) { - String lowerName = headerName.toLowerCase(); - // 排除 Host、Content-Length 等头 - return lowerName.equals("host") || - lowerName.equals("content-length") || - lowerName.equals("transfer-encoding"); + public ApiResponse proxyPatch(HttpServletRequest request) { + return algorithmClient.proxyPatch(request); } } diff --git a/src/main/java/com/gis/xian/controller/ReportOutputController.java b/src/main/java/com/gis/xian/controller/ReportOutputController.java new file mode 100644 index 0000000..d84fb34 --- /dev/null +++ b/src/main/java/com/gis/xian/controller/ReportOutputController.java @@ -0,0 +1,27 @@ +package com.gis.xian.controller; + +import com.gis.xian.domain.ApiResponse; +import com.gis.xian.service.ReportOutputService; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/report/output") +public class ReportOutputController { + + @Resource + private ReportOutputService reportOutputService; + + /** + * 输出暴雨报告 + * @param id 模拟id + * @return 暴雨报告链接 + */ + @PostMapping("/rainfall/{id}") + public ApiResponse rainfall(@PathVariable String id) { + return ApiResponse.ok(reportOutputService.outputRainReport(Long.parseLong(id))); + } +} diff --git a/src/main/java/com/gis/xian/dto/BaseCondition.java b/src/main/java/com/gis/xian/dto/BaseCondition.java new file mode 100644 index 0000000..cac3f73 --- /dev/null +++ b/src/main/java/com/gis/xian/dto/BaseCondition.java @@ -0,0 +1,67 @@ +package com.gis.xian.dto; + +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 预测条件基类 — 对应 xian_inference_result.condition JSONB 字段 + */ +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class BaseCondition { + + /** + * 点位ID列表 + */ + @JSONField(name = "point_ids") + private List pointIds; + + /** + * 行政区划代码 + */ + @JSONField(name = "region_code") + private String regionCode; + + /** + * 事件发生时间 + */ + @JSONField(name = "occurred_time", format = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime occurredTime; + + /** + * 震级 (Richter) + */ + private Float magnitude; + + /** + * 震源深度 (km) + */ + private Float depth; + + /** + * 震中经度 + */ + @JSONField(name = "epicenter_lon") + private Float epicenterLon; + + /** + * 震中纬度 + */ + @JSONField(name = "epicenter_lat") + private Float epicenterLat; + + /** + * 累计降雨量(mm) + */ + private Float rainfall; + + /** + * 降雨持续时间(h) + */ + private Float duration; +} diff --git a/src/main/java/com/gis/xian/dto/RainfallDistrictSummaryResponseDTO.java b/src/main/java/com/gis/xian/dto/RainfallDistrictSummaryResponseDTO.java new file mode 100644 index 0000000..345913e --- /dev/null +++ b/src/main/java/com/gis/xian/dto/RainfallDistrictSummaryResponseDTO.java @@ -0,0 +1,30 @@ +package com.gis.xian.dto; + +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Data; + +@Data +@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) +public class RainfallDistrictSummaryResponseDTO { + /** + * 行政区名称 + */ + @JSONField(name = "district_name") + private String districtName; + /** + * 行政区编码 + */ + @JSONField(name = "district_code") + private String districtCode; + /** + * 雨量 + */ + private String rainfall; + /** + * 持续时间 + */ + @JSONField(name = "duration_hours") + private String durationHours; +} diff --git a/src/main/java/com/gis/xian/dto/RiskAndHiddenSpotDTO.java b/src/main/java/com/gis/xian/dto/RiskAndHiddenSpotDTO.java new file mode 100644 index 0000000..1666ec7 --- /dev/null +++ b/src/main/java/com/gis/xian/dto/RiskAndHiddenSpotDTO.java @@ -0,0 +1,9 @@ +package com.gis.xian.dto; + +import lombok.Data; + +@Data +public class RiskAndHiddenSpotDTO { + private String districtName; + private String number; +} diff --git a/src/main/java/com/gis/xian/entity/XianInferenceResult.java b/src/main/java/com/gis/xian/entity/XianInferenceResult.java index 21bf03d..397a3fb 100644 --- a/src/main/java/com/gis/xian/entity/XianInferenceResult.java +++ b/src/main/java/com/gis/xian/entity/XianInferenceResult.java @@ -1,11 +1,15 @@ package com.gis.xian.entity; -import java.util.Date; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.time.LocalDateTime; /** * 推理结果表 - * @TableName xian_inference_result */ +@Data +@TableName(value = "xian_inference_result", autoResultMap = true) public class XianInferenceResult { /** @@ -19,14 +23,14 @@ public class XianInferenceResult { private String name; /** - * 事件类型 + * 事件类型:rainfall / earthquake */ private String eventType; /** * 发生时间 */ - private Date occurredTime; + private LocalDateTime occurredTime; /** * 操作类型 @@ -36,186 +40,20 @@ public class XianInferenceResult { /** * 推理结果(JSONB) */ - private Object result; + private String result; /** - * 条件(JSONB) + * 预测条件(JSONB) */ - private Object condition; - - /** - * 文件路径(JSONB) - */ - private Object filePath; + private String condition; /** * 创建时间 */ - private Date createTime; + private LocalDateTime createTime; /** - * 是否删除(0: 未删除, 1: 已删除) + * 是否删除(0:未删除, 1:已删除) */ private Integer isDelete; - - // 构造方法 - public XianInferenceResult() { - } - - public XianInferenceResult(Long id, String name, String eventType, Date occurredTime, - String operationType, Object result, Object condition, - Object filePath, Date createTime, Integer isDelete) { - this.id = id; - this.name = name; - this.eventType = eventType; - this.occurredTime = occurredTime; - this.operationType = operationType; - this.result = result; - this.condition = condition; - this.filePath = filePath; - this.createTime = createTime; - this.isDelete = isDelete; - } - - // Getter 和 Setter 方法 - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getEventType() { - return eventType; - } - - public void setEventType(String eventType) { - this.eventType = eventType; - } - - public Date getOccurredTime() { - return occurredTime; - } - - public void setOccurredTime(Date occurredTime) { - this.occurredTime = occurredTime; - } - - public String getOperationType() { - return operationType; - } - - public void setOperationType(String operationType) { - this.operationType = operationType; - } - - public Object getResult() { - return result; - } - - public void setResult(Object result) { - this.result = result; - } - - public Object getCondition() { - return condition; - } - - public void setCondition(Object condition) { - this.condition = condition; - } - - public Object getFilePath() { - return filePath; - } - - public void setFilePath(Object filePath) { - this.filePath = filePath; - } - - public Date getCreateTime() { - return createTime; - } - - public void setCreateTime(Date createTime) { - this.createTime = createTime; - } - - public Integer getIsDelete() { - return isDelete; - } - - public void setIsDelete(Integer isDelete) { - this.isDelete = isDelete; - } - - @Override - public boolean equals(Object that) { - if (this == that) { - return true; - } - if (that == null) { - return false; - } - if (getClass() != that.getClass()) { - return false; - } - XianInferenceResult other = (XianInferenceResult) that; - return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId())) - && (this.getName() == null ? other.getName() == null : this.getName().equals(other.getName())) - && (this.getEventType() == null ? other.getEventType() == null : this.getEventType().equals(other.getEventType())) - && (this.getOccurredTime() == null ? other.getOccurredTime() == null : this.getOccurredTime().equals(other.getOccurredTime())) - && (this.getOperationType() == null ? other.getOperationType() == null : this.getOperationType().equals(other.getOperationType())) - && (this.getResult() == null ? other.getResult() == null : this.getResult().equals(other.getResult())) - && (this.getCondition() == null ? other.getCondition() == null : this.getCondition().equals(other.getCondition())) - && (this.getFilePath() == null ? other.getFilePath() == null : this.getFilePath().equals(other.getFilePath())) - && (this.getCreateTime() == null ? other.getCreateTime() == null : this.getCreateTime().equals(other.getCreateTime())) - && (this.getIsDelete() == null ? other.getIsDelete() == null : this.getIsDelete().equals(other.getIsDelete())); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); - result = prime * result + ((getName() == null) ? 0 : getName().hashCode()); - result = prime * result + ((getEventType() == null) ? 0 : getEventType().hashCode()); - result = prime * result + ((getOccurredTime() == null) ? 0 : getOccurredTime().hashCode()); - result = prime * result + ((getOperationType() == null) ? 0 : getOperationType().hashCode()); - result = prime * result + ((getResult() == null) ? 0 : getResult().hashCode()); - result = prime * result + ((getCondition() == null) ? 0 : getCondition().hashCode()); - result = prime * result + ((getFilePath() == null) ? 0 : getFilePath().hashCode()); - result = prime * result + ((getCreateTime() == null) ? 0 : getCreateTime().hashCode()); - result = prime * result + ((getIsDelete() == null) ? 0 : getIsDelete().hashCode()); - return result; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(getClass().getSimpleName()); - sb.append(" ["); - sb.append("Hash = ").append(hashCode()); - sb.append(", id=").append(id); - sb.append(", name=").append(name); - sb.append(", eventType=").append(eventType); - sb.append(", occurredTime=").append(occurredTime); - sb.append(", operationType=").append(operationType); - sb.append(", result=").append(result); - sb.append(", condition=").append(condition); - sb.append(", filePath=").append(filePath); - sb.append(", createTime=").append(createTime); - sb.append(", isDelete=").append(isDelete); - sb.append("]"); - return sb.toString(); - } } diff --git a/src/main/java/com/gis/xian/entity/XianMeteorology.java b/src/main/java/com/gis/xian/entity/XianMeteorology.java new file mode 100644 index 0000000..2e09f8b --- /dev/null +++ b/src/main/java/com/gis/xian/entity/XianMeteorology.java @@ -0,0 +1,134 @@ +package com.gis.xian.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 java.io.Serializable; +import java.time.LocalDateTime; +import lombok.Data; + +/** + * 西安市气象局-实况-小时实况 + * @TableName xian_meteorology + */ +@TableName(value ="xian_meteorology") +@Data +public class XianMeteorology implements Serializable { + /** + * 主键ID,自增 + */ + @TableId(value = "id") + private Long id; + + /** + * 区站名称 + */ + @TableField(value = "station_name") + private String stationName; + + /** + * 水平能见度(人工) + */ + @TableField(value = "visibility") + private Double visibility; + + /** + * 区域编码 + */ + @TableField(value = "area_code") + private String areaCode; + + /** + * 最大风速 + */ + @TableField(value = "max_wind_speed") + private Double maxWindSpeed; + + /** + * 过去1小时降水量,单位:毫米 + */ + @TableField(value = "rainfall_1h") + private Double rainfall1h; + + /** + * 气压 + */ + @TableField(value = "pressure") + private Double pressure; + + /** + * 最大风速的风向 + */ + @TableField(value = "max_wind_direction") + private Double maxWindDirection; + + /** + * 极大风速的风向 + */ + @TableField(value = "inst_max_wind_direction") + private Double instMaxWindDirection; + + /** + * 区站号 + */ + @TableField(value = "station_id") + private String stationId; + + /** + * 温度/气温,单位:摄氏度 + */ + @TableField(value = "temperature") + private Double temperature; + + /** + * 相对湿度 + */ + @TableField(value = "relative_humidity") + private Double relativeHumidity; + + /** + * 日期(加8小时为数据对应时间) + */ + @TableField(value = "datetime") + private Long datetime; + + /** + * 经度 + */ + @TableField(value = "lon") + private Double lon; + + /** + * 纬度 + */ + @TableField(value = "lat") + private Double lat; + + /** + * 地理位置点 + */ + @TableField(value = "geom") + private Object geom; + + /** + * 创建时间 + */ + @TableField(value = "create_time") + private LocalDateTime createTime; + + /** + * 更新时间 + */ + @TableField(value = "update_time") + private LocalDateTime updateTime; + + /** + * 逻辑删除标识,0未删除,1已删除 + */ + @TableField(value = "is_delete") + private Integer isDelete; + + @TableField(exist = false) + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/src/main/java/com/gis/xian/mapper/XianHiddenDangerSpotsMapper.java b/src/main/java/com/gis/xian/mapper/XianHiddenDangerSpotsMapper.java index b1beddc..1ab8677 100644 --- a/src/main/java/com/gis/xian/mapper/XianHiddenDangerSpotsMapper.java +++ b/src/main/java/com/gis/xian/mapper/XianHiddenDangerSpotsMapper.java @@ -1,5 +1,6 @@ package com.gis.xian.mapper; +import com.gis.xian.dto.RiskAndHiddenSpotDTO; import com.gis.xian.entity.XianHiddenDangerSpots; import java.util.List; @@ -13,6 +14,7 @@ import java.util.List; public interface XianHiddenDangerSpotsMapper { /** * 获取所有基础点:滑坡、泥石流、山洪、内涝 + * * @param disasterType 具体灾害类型(landslide-滑坡, debris_flow-泥石流, flash_flood-山洪, water_logging-内涝),可选 * @return 基础点列表 */ @@ -20,10 +22,27 @@ public interface XianHiddenDangerSpotsMapper { /** * 根据id获取隐患点详情 + * * @param id 隐患点id * @return 隐患点详情 */ XianHiddenDangerSpots getPointDetailById(Long id); + + /** + * 根据行政区获取隐患点数量 + * + * @param districtNames 行政名称列表 + * @return 隐患点数量 + */ + List queryHiddenDangerNumberByDistrictName(List districtNames); + + /** + * 根据id获取隐患点详情 + * + * @param ids 隐患点id列表 + * @return 隐患点详情 + */ + List getPointDetailByIds(List ids); } diff --git a/src/main/java/com/gis/xian/mapper/XianInferenceResultMapper.java b/src/main/java/com/gis/xian/mapper/XianInferenceResultMapper.java index 41b4f02..898fdb7 100644 --- a/src/main/java/com/gis/xian/mapper/XianInferenceResultMapper.java +++ b/src/main/java/com/gis/xian/mapper/XianInferenceResultMapper.java @@ -1,5 +1,6 @@ package com.gis.xian.mapper; +import com.gis.xian.entity.XianInferenceResult; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; @@ -13,9 +14,25 @@ import org.apache.ibatis.annotations.Param; public interface XianInferenceResultMapper { /** * 根据id和pointId获取概率 - * @param id 预测结果id + * + * @param id 预测结果id * @param pointId 隐患点/风险点id和类型 * @return 预测概率 */ String getProbabilityByIdAndPointId(@Param("id") Long id, @Param("pointId") String pointId); + + /** + * 根据id获取预测结果 + * + * @param id 预测结果id + * @return 预测结果 + */ + XianInferenceResult selectById(Long id); + + /** + * 获取预测结果数量 + * + * @return 预测结果数量 + */ + Integer getTheNumberOfPredictedPoints(); } diff --git a/src/main/java/com/gis/xian/mapper/XianMeteorologyMapper.java b/src/main/java/com/gis/xian/mapper/XianMeteorologyMapper.java new file mode 100644 index 0000000..befe2ad --- /dev/null +++ b/src/main/java/com/gis/xian/mapper/XianMeteorologyMapper.java @@ -0,0 +1,11 @@ +package com.gis.xian.mapper; + +/** + * @author wzy + * @description 针对表【xian_meteorology(西安市气象局-实况-小时实况)】的数据库操作Mapper + * @createDate 2026-06-27 10:28:34 + * @Entity com.gis.xian.entity.XianMeteorology + */ +public interface XianMeteorologyMapper { + +} diff --git a/src/main/java/com/gis/xian/mapper/XianRiskSpotsMapper.java b/src/main/java/com/gis/xian/mapper/XianRiskSpotsMapper.java index f688c75..2a1d19f 100644 --- a/src/main/java/com/gis/xian/mapper/XianRiskSpotsMapper.java +++ b/src/main/java/com/gis/xian/mapper/XianRiskSpotsMapper.java @@ -1,18 +1,20 @@ package com.gis.xian.mapper; +import com.gis.xian.dto.RiskAndHiddenSpotDTO; import com.gis.xian.entity.XianRiskSpots; import java.util.List; /** -* @author wzy -* @description 针对表【xian_risk_spots(地质灾害风险区)】的数据库操作Mapper -* @createDate 2026-04-11 10:38:29 -* @Entity com.gis.xian.entity.XianRiskSpots -*/ + * @author wzy + * @description 针对表【xian_risk_spots(地质灾害风险区)】的数据库操作Mapper + * @createDate 2026-04-11 10:38:29 + * @Entity com.gis.xian.entity.XianRiskSpots + */ public interface XianRiskSpotsMapper { /** * 获取所有风险点基础信息 + * * @return 风险点基础列表 */ List getBasePoints(); @@ -20,10 +22,19 @@ public interface XianRiskSpotsMapper { /** * 根据id获取风险点详情 + * * @param id 风险点id * @return 风险点详情 */ XianRiskSpots getPointDetailById(Long id); + + /** + * 根据行政区获取风险点数量 + * + * @param districtNames 行政名称列表 + * @return 风险点数量 + */ + List queryRiskNumberByDistrictName(List districtNames); } diff --git a/src/main/java/com/gis/xian/service/ReportOutputService.java b/src/main/java/com/gis/xian/service/ReportOutputService.java new file mode 100644 index 0000000..bbe9048 --- /dev/null +++ b/src/main/java/com/gis/xian/service/ReportOutputService.java @@ -0,0 +1,10 @@ +package com.gis.xian.service; + +public interface ReportOutputService { + /** + * 产出暴雨报告 + * @param id 模拟id + * @return 报告链接 + */ + String outputRainReport(Long id); +} diff --git a/src/main/java/com/gis/xian/service/ex/ReportParameterException.java b/src/main/java/com/gis/xian/service/ex/ReportParameterException.java new file mode 100644 index 0000000..3d2eeed --- /dev/null +++ b/src/main/java/com/gis/xian/service/ex/ReportParameterException.java @@ -0,0 +1,22 @@ +package com.gis.xian.service.ex; + +public class ReportParameterException extends ServiceException { + public ReportParameterException() { + } + + public ReportParameterException(String message) { + super(message); + } + + public ReportParameterException(String message, Throwable cause) { + super(message, cause); + } + + public ReportParameterException(Throwable cause) { + super(cause); + } + + public ReportParameterException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } +} diff --git a/src/main/java/com/gis/xian/service/impl/IReportOutputServiceImpl.java b/src/main/java/com/gis/xian/service/impl/IReportOutputServiceImpl.java new file mode 100644 index 0000000..e2beb3b --- /dev/null +++ b/src/main/java/com/gis/xian/service/impl/IReportOutputServiceImpl.java @@ -0,0 +1,238 @@ +package com.gis.xian.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.TypeReference; +import com.gis.xian.config.AlgorithmClient; +import com.gis.xian.config.ReportProperties; +import com.gis.xian.domain.ApiResponse; +import com.gis.xian.dto.BaseCondition; +import com.gis.xian.dto.RainfallDistrictSummaryResponseDTO; +import com.gis.xian.dto.RiskAndHiddenSpotDTO; +import com.gis.xian.entity.XianHiddenDangerSpots; +import com.gis.xian.entity.XianInferenceResult; +import com.gis.xian.mapper.XianHiddenDangerSpotsMapper; +import com.gis.xian.mapper.XianInferenceResultMapper; +import com.gis.xian.mapper.XianRiskSpotsMapper; +import com.gis.xian.service.ReportOutputService; +import com.gis.xian.service.ex.ReportParameterException; +import com.gis.xian.utils.DateTimeUtils; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Service +@Slf4j +public class IReportOutputServiceImpl implements ReportOutputService { + + @Resource + private XianInferenceResultMapper xianInferenceResultMapper; + + @Resource + private AlgorithmClient algorithmClient; + + @Resource + private XianRiskSpotsMapper xianRiskSpotsMapper; + + @Resource + private XianHiddenDangerSpotsMapper xianHiddenDangerSpotsMapper; + + @Resource + private ReportProperties reportProperties; + + @Override + public String outputRainReport(Long id) { + // 存储替换模版的map + Map replaceTemplateMap = new HashMap<>(); + + // 中高风险隐患点id + Map> hiddenDangerSpotsIds = null; + + // 获取模拟信息 + XianInferenceResult simulationInfo = xianInferenceResultMapper.selectById(id); + + // 隐患点id + hiddenDangerSpotsIds = getHiddenDangerSpotsIds(simulationInfo); + + // 获取高风险隐患点信息 + List highHiddenDangerSpots = xianHiddenDangerSpotsMapper.getPointDetailByIds(hiddenDangerSpotsIds.get("high")); + + // 获取中风险隐患点信息 + List middleHiddenDangerSpots = xianHiddenDangerSpotsMapper.getPointDetailByIds(hiddenDangerSpotsIds.get("middle")); + + // 获取降雨信息 + ApiResponse> rainData = algorithmClient.post("/rainfall/district-summary", Map.of("inference_id", 1), + new ParameterizedTypeReference<>() { + }); + + // 获取行政区划名称 + List districtNames = getDistrictNames(rainData.getData()); + + // 获取风险区和隐患点数量 + List riskNumbers = xianRiskSpotsMapper.queryRiskNumberByDistrictName(districtNames); + List hiddenDangerNumbers = xianHiddenDangerSpotsMapper.queryHiddenDangerNumberByDistrictName(districtNames); + + // 获取预测点数量 + Integer predictPointNum = xianInferenceResultMapper.getTheNumberOfPredictedPoints(); + + // 生成降雨报告模版 + generateRainReportTemplate(replaceTemplateMap, simulationInfo, highHiddenDangerSpots, middleHiddenDangerSpots, rainData.getData(), districtNames, riskNumbers, hiddenDangerNumbers, predictPointNum); + System.out.println(replaceTemplateMap); + return ""; + } + + /** + * 获取隐患点id + * + * @param simulationInfo 模拟信息 + * @return 隐患点id + */ + private Map> getHiddenDangerSpotsIds(XianInferenceResult simulationInfo) { + // 预测结果 + Map probabilities = JSON.parseObject(simulationInfo.getResult(), new TypeReference<>() { + }); + + // id + Map> ids = new HashMap<>(); + ids.put("high", new ArrayList<>()); + ids.put("middle", new ArrayList<>()); + + for (String point : probabilities.keySet()) { + Long id = Long.parseLong(point.split("_")[0]); + if (probabilities.get(point) >= 70) { + ids.get("high").add(id); + } else if (probabilities.get(point) >= 50) { + ids.get("middle").add(id); + } + } + return ids; + } + + /** + * 获取前3个风险地区名称 + * + * @param rainData 降雨数据 + * @return 行政区名称 + */ + private List getDistrictNames(List rainData) { + return rainData.stream() + .filter(dto -> dto.getRainfall() != null && !dto.getRainfall().isEmpty()) + // 按降雨量降序排序 + .sorted((dto1, dto2) -> { + double r1 = Double.parseDouble(dto1.getRainfall()); + double r2 = Double.parseDouble(dto2.getRainfall()); + return Double.compare(r2, r1); // 降序 + }) + .limit(3) + .map(RainfallDistrictSummaryResponseDTO::getDistrictName) + .collect(Collectors.toList()); + } + + private void generateRainReportTemplate( + Map m, + XianInferenceResult simulationInfo, + List highHiddenDangerSpots, + List middleHiddenDangerSpots, + List rainData, + List districtNames, + List riskNumbers, + List hiddenDangerNumbers, + Integer predictPointNum + ) { + + try { + BaseCondition condition = JSON.parseObject(simulationInfo.getCondition(), BaseCondition.class); + List hiddenDangerSpots = Stream.concat(highHiddenDangerSpots.stream(), middleHiddenDangerSpots.stream()).collect(Collectors.toList()); + + m.put("报告时间", DateTimeUtils.datetimeFormat(simulationInfo.getOccurredTime(), "MM月dd日HH时mm分")); + m.put("降雨时间", DateTimeUtils.datetimeFormat(simulationInfo.getOccurredTime(), "yyyy年MM月dd日HH时")); + m.put("降雨地区", String.join("、", districtNames)); + m.put("持续时间", condition.getDuration() == null ? 72 : condition.getDuration()); + m.put("降雨量", getRainfall(rainData, districtNames)); + m.put("降雨集中区域", districtNames.isEmpty() ? "" : districtNames.get(0)); + m.put("风险区数量", riskNumbers.size()); + m.put("隐患点数量", hiddenDangerNumbers.size()); + m.put("致灾因子", String.join("、", reportProperties.getDisasterCausingFactors().getRainfall().getType()) + "等"); + m.put("致灾因子数量", reportProperties.getDisasterCausingFactors().getRainfall().getNumber()); + m.put("预测点数量", predictPointNum); + m.put("灾害链", getRainfallDisasterChain(hiddenDangerSpots)); + m.put("具体风险地区", getRainfallDisasterChainPosition(hiddenDangerSpots)); + m.put("高风险区域数量", highHiddenDangerSpots.size()); + m.put("次生灾害类型", getSecondaryDisasterType(highHiddenDangerSpots)); + m.put("hasDisasters", !hiddenDangerSpots.isEmpty()); + m.put("hasLandslide", highHiddenDangerSpots.stream().anyMatch(d -> "滑坡".equals(d.getDisasterType()))); + m.put("滑坡中高风险街道", highHiddenDangerSpots.stream().filter(d -> "滑坡".equals(d.getDisasterType())).map(XianHiddenDangerSpots::getVillage).collect(Collectors.joining("、"))); + } catch (Exception e) { + log.error("生成暴雨报告出错:{}", e.getMessage()); + throw new ReportParameterException("生成暴雨报告出错:" + e.getMessage()); + } + + } + + /** + * 获取降雨量 + * + * @param rainData 降雨数据 + * @param districtNames 行政区名称 + * @return 降雨量 + */ + private String getRainfall(List rainData, List districtNames) { + Map map = rainData.stream() + .collect(Collectors.toMap( + RainfallDistrictSummaryResponseDTO::getDistrictName, + RainfallDistrictSummaryResponseDTO::getRainfall, + (v1, v2) -> v1 + )); + return districtNames.stream() + .map(map::get) + .filter(Objects::nonNull) + .collect(Collectors.joining("mm、")) + "mm"; + } + + /** + * 获取暴雨灾害链 + * + * @param hiddenDangerSpots 隐藏点信息 + * @return 暴雨Chain + */ + private String getRainfallDisasterChain(List hiddenDangerSpots) { + return hiddenDangerSpots.stream() + .map(XianHiddenDangerSpots::getDisasterType) + .filter(Objects::nonNull) + .distinct() + .map(name -> "暴雨-" + name) + .collect(Collectors.joining("、")); + } + + /** + * 获取暴雨灾害链位置 + * + * @param hiddenDangerSpots 隐藏点信息 + * @return 暴雨灾害链位置 + */ + private String getRainfallDisasterChainPosition(List hiddenDangerSpots) { + return hiddenDangerSpots.stream() + .map(XianHiddenDangerSpots::getVillage) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.joining("、")); + } + + /** + * 获取次生灾害类型 + * + * @param hiddenDangerSpots 隐藏点信息 + * @return 次生灾害类型 + */ + private String getSecondaryDisasterType(List hiddenDangerSpots) { + return hiddenDangerSpots.stream() + .map(XianHiddenDangerSpots::getDisasterType) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.joining("、")); + } +} diff --git a/src/main/java/com/gis/xian/task/InitializeData.java b/src/main/java/com/gis/xian/task/InitializeData.java index 76cacf8..a4029b6 100644 --- a/src/main/java/com/gis/xian/task/InitializeData.java +++ b/src/main/java/com/gis/xian/task/InitializeData.java @@ -19,7 +19,7 @@ import java.util.concurrent.Executor; /** * 初始化数据 */ -@Component +//@Component @Slf4j public class InitializeData { diff --git a/src/main/java/com/gis/xian/utils/DateTimeUtils.java b/src/main/java/com/gis/xian/utils/DateTimeUtils.java new file mode 100644 index 0000000..895ed1b --- /dev/null +++ b/src/main/java/com/gis/xian/utils/DateTimeUtils.java @@ -0,0 +1,18 @@ +package com.gis.xian.utils; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class DateTimeUtils { + + /** + * 格式化日期时间 + * + * @param datetime 日期时间 + * @param format 格式 + * @return 格式化后的日期时间 + */ + public static String datetimeFormat(LocalDateTime datetime, String format) { + return datetime.format(DateTimeFormatter.ofPattern(format)); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index dc6cdec..0f00ec8 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -13,3 +13,13 @@ mybatis-plus: map-underscore-to-camel-case: true # 添加 TypeHandler 扫描包路径 type-handlers-package: com.gis.xian.handler + +# 报告配置 +report: + disaster-causing-factors: + rainfall: + type: [ "降雨强度", "持续时间", "累计降雨量", "高程", "坡度", "坡向", "土壤分类", "岩性", "土地利用类型", "不透水率", "植被覆盖指数", "距河道距离", "管网密度" ] + number: 19 + earthquake: + type: [ "震度", "震源深度", "距离震中位置", "高程", "坡度", "坡向", "土壤分类", "岩性", "土地利用类型", "植被覆盖指数", "距离断裂带距离" ] + number: 17 \ No newline at end of file diff --git a/src/main/resources/com/gis/xian/mapper/XianHiddenDangerSpotsMapper.xml b/src/main/resources/com/gis/xian/mapper/XianHiddenDangerSpotsMapper.xml index ca52c6f..1481037 100644 --- a/src/main/resources/com/gis/xian/mapper/XianHiddenDangerSpotsMapper.xml +++ b/src/main/resources/com/gis/xian/mapper/XianHiddenDangerSpotsMapper.xml @@ -5,24 +5,29 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + @@ -55,10 +60,42 @@ + + + + diff --git a/src/main/resources/com/gis/xian/mapper/XianInferenceResultMapper.xml b/src/main/resources/com/gis/xian/mapper/XianInferenceResultMapper.xml index 59e530a..9da40c7 100644 --- a/src/main/resources/com/gis/xian/mapper/XianInferenceResultMapper.xml +++ b/src/main/resources/com/gis/xian/mapper/XianInferenceResultMapper.xml @@ -5,16 +5,15 @@ - - - - - - - - - - + + + + + + + + + @@ -25,4 +24,28 @@ + + + + \ No newline at end of file diff --git a/src/main/resources/com/gis/xian/mapper/XianMeteorologyMapper.xml b/src/main/resources/com/gis/xian/mapper/XianMeteorologyMapper.xml new file mode 100644 index 0000000..a9366ad --- /dev/null +++ b/src/main/resources/com/gis/xian/mapper/XianMeteorologyMapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/com/gis/xian/mapper/XianRiskSpotsMapper.xml b/src/main/resources/com/gis/xian/mapper/XianRiskSpotsMapper.xml index 7a367fe..491a6c5 100644 --- a/src/main/resources/com/gis/xian/mapper/XianRiskSpotsMapper.xml +++ b/src/main/resources/com/gis/xian/mapper/XianRiskSpotsMapper.xml @@ -5,28 +5,33 @@ - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -39,11 +44,26 @@ + diff --git a/src/main/resources/config/customize/application-customize-dev.yml b/src/main/resources/config/customize/application-customize-dev.yml index 9912192..2a91d8a 100644 --- a/src/main/resources/config/customize/application-customize-dev.yml +++ b/src/main/resources/config/customize/application-customize-dev.yml @@ -4,4 +4,6 @@ server: algorithm: server: - url: "http://localhost:8082" \ No newline at end of file + url: "http://localhost:8082" + connect-timeout: 5 + read-timeout: 120 \ No newline at end of file diff --git a/src/main/resources/config/customize/application-customize-prod.yml b/src/main/resources/config/customize/application-customize-prod.yml index ee478bc..e7643a2 100644 --- a/src/main/resources/config/customize/application-customize-prod.yml +++ b/src/main/resources/config/customize/application-customize-prod.yml @@ -4,4 +4,6 @@ server: algorithm: server: - url: "http://localhost:8081" \ No newline at end of file + url: "http://localhost:8081" + connect-timeout: 5 + read-timeout: 120 \ No newline at end of file diff --git a/src/main/resources/template/rainfall/rainfall-template.docx b/src/main/resources/template/rainfall/rainfall-template.docx new file mode 100644 index 0000000..17d3649 Binary files /dev/null and b/src/main/resources/template/rainfall/rainfall-template.docx differ diff --git a/src/main/resources/template/rainfall/~$infall-template.docx b/src/main/resources/template/rainfall/~$infall-template.docx new file mode 100644 index 0000000..9e06b61 Binary files /dev/null and b/src/main/resources/template/rainfall/~$infall-template.docx differ