部分暴雨灾害链报告产出,表格信息前。

This commit is contained in:
wzy-warehouse
2026-06-28 11:27:33 +08:00
parent 20bfead821
commit 2ef96f812f
29 changed files with 1048 additions and 425 deletions
+8
View File
@@ -13,6 +13,7 @@
<bcprov-jdk15to18.version>1.82</bcprov-jdk15to18.version>
<fastjson.version>2.0.60</fastjson.version>
<mybatis-plus.version>3.5.9</mybatis-plus.version>
<poi-tl-version>1.12.2</poi-tl-version>
</properties>
<parent>
@@ -120,6 +121,13 @@
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- word模板 -->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>${poi-tl-version}</version>
</dependency>
<!-- 测试框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -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<Object> proxyGet(HttpServletRequest request) {
return proxyRequest(request, HttpMethod.GET);
}
public ApiResponse<Object> proxyPost(HttpServletRequest request) {
return proxyRequest(request, HttpMethod.POST);
}
public ApiResponse<Object> proxyPut(HttpServletRequest request) {
return proxyRequest(request, HttpMethod.PUT);
}
public ApiResponse<Object> proxyDelete(HttpServletRequest request) {
return proxyRequest(request, HttpMethod.DELETE);
}
public ApiResponse<Object> proxyPatch(HttpServletRequest request) {
return proxyRequest(request, HttpMethod.PATCH);
}
@SuppressWarnings("unchecked")
private ApiResponse<Object> 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<String> 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> T get(String path, Class<T> responseType) {
String url = props.getUrl() + path;
log.info("GET {}", url);
return restClient.get().uri(url).retrieve().body(responseType);
}
public <T> T get(String path, ParameterizedTypeReference<T> responseType) {
String url = props.getUrl() + path;
log.info("GET {}", url);
return restClient.get().uri(url).retrieve().body(responseType);
}
public <T> T post(String path, Object body, Class<T> responseType) {
String url = props.getUrl() + path;
log.info("POST {} body={}", url, body);
return restClient.post().uri(url).body(body).retrieve().body(responseType);
}
public <T> T post(String path, Object body, ParameterizedTypeReference<T> 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<String> 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<Object> parseResponseBody(String body) {
if (body == null || body.isEmpty()) {
return ApiResponse.ok(null);
}
try {
Map<String, Object> 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);
}
}
@@ -16,4 +16,14 @@ public class AlgorithmServerProperties {
* 算法服务器地址
*/
private String url;
/**
* 连接超时(秒)
*/
private int connectTimeout;
/**
* 读取超时(秒)
*/
private int readTimeout;
}
@@ -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<String> type;
private Integer number;
}
}
@@ -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<Object> proxyGet(HttpServletRequest request) {
return proxyRequest(request, HttpMethod.GET);
return algorithmClient.proxyGet(request);
}
/**
* 处理所有 POST 请求
*/
@PostMapping("/**")
public ApiResponse<Object> proxyPost(HttpServletRequest request) throws IOException {
return proxyRequest(request, HttpMethod.POST);
public ApiResponse<Object> proxyPost(HttpServletRequest request) {
return algorithmClient.proxyPost(request);
}
/**
* 处理所有 PUT 请求
*/
@PutMapping("/**")
public ApiResponse<Object> proxyPut(HttpServletRequest request) throws IOException {
return proxyRequest(request, HttpMethod.PUT);
public ApiResponse<Object> proxyPut(HttpServletRequest request) {
return algorithmClient.proxyPut(request);
}
/**
* 处理所有 DELETE 请求
*/
@DeleteMapping("/**")
public ApiResponse<Object> proxyDelete(HttpServletRequest request) {
return proxyRequest(request, HttpMethod.DELETE);
return algorithmClient.proxyDelete(request);
}
/**
* 处理所有 PATCH 请求
*/
@PatchMapping("/**")
public ApiResponse<Object> proxyPatch(HttpServletRequest request) throws IOException {
return proxyRequest(request, HttpMethod.PATCH);
}
/**
* 通用的请求代理方法
*/
@SuppressWarnings("unchecked")
private ApiResponse<Object> 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<Object> entity = new HttpEntity<>(requestBody, headers);
// 发送请求到算法服务器
ResponseEntity<String> 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<String, Object> 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<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
// 排除一些不应该转发的头
if (!shouldExcludeHeader(headerName)) {
Enumeration<String> 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<Object> proxyPatch(HttpServletRequest request) {
return algorithmClient.proxyPatch(request);
}
}
@@ -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<String> rainfall(@PathVariable String id) {
return ApiResponse.ok(reportOutputService.outputRainReport(Long.parseLong(id)));
}
}
@@ -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<Integer> 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;
}
@@ -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;
}
@@ -0,0 +1,9 @@
package com.gis.xian.dto;
import lombok.Data;
@Data
public class RiskAndHiddenSpotDTO {
private String districtName;
private String number;
}
@@ -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();
}
}
@@ -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;
}
@@ -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<RiskAndHiddenSpotDTO> queryHiddenDangerNumberByDistrictName(List<String> districtNames);
/**
* 根据id获取隐患点详情
*
* @param ids 隐患点id列表
* @return 隐患点详情
*/
List<XianHiddenDangerSpots> getPointDetailByIds(List<Long> ids);
}
@@ -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();
}
@@ -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 {
}
@@ -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<XianRiskSpots> getBasePoints();
@@ -20,10 +22,19 @@ public interface XianRiskSpotsMapper {
/**
* 根据id获取风险点详情
*
* @param id 风险点id
* @return 风险点详情
*/
XianRiskSpots getPointDetailById(Long id);
/**
* 根据行政区获取风险点数量
*
* @param districtNames 行政名称列表
* @return 风险点数量
*/
List<RiskAndHiddenSpotDTO> queryRiskNumberByDistrictName(List<String> districtNames);
}
@@ -0,0 +1,10 @@
package com.gis.xian.service;
public interface ReportOutputService {
/**
* 产出暴雨报告
* @param id 模拟id
* @return 报告链接
*/
String outputRainReport(Long id);
}
@@ -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);
}
}
@@ -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<String, Object> replaceTemplateMap = new HashMap<>();
// 中高风险隐患点id
Map<String, List<Long>> hiddenDangerSpotsIds = null;
// 获取模拟信息
XianInferenceResult simulationInfo = xianInferenceResultMapper.selectById(id);
// 隐患点id
hiddenDangerSpotsIds = getHiddenDangerSpotsIds(simulationInfo);
// 获取高风险隐患点信息
List<XianHiddenDangerSpots> highHiddenDangerSpots = xianHiddenDangerSpotsMapper.getPointDetailByIds(hiddenDangerSpotsIds.get("high"));
// 获取中风险隐患点信息
List<XianHiddenDangerSpots> middleHiddenDangerSpots = xianHiddenDangerSpotsMapper.getPointDetailByIds(hiddenDangerSpotsIds.get("middle"));
// 获取降雨信息
ApiResponse<List<RainfallDistrictSummaryResponseDTO>> rainData = algorithmClient.post("/rainfall/district-summary", Map.of("inference_id", 1),
new ParameterizedTypeReference<>() {
});
// 获取行政区划名称
List<String> districtNames = getDistrictNames(rainData.getData());
// 获取风险区和隐患点数量
List<RiskAndHiddenSpotDTO> riskNumbers = xianRiskSpotsMapper.queryRiskNumberByDistrictName(districtNames);
List<RiskAndHiddenSpotDTO> 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<String, List<Long>> getHiddenDangerSpotsIds(XianInferenceResult simulationInfo) {
// 预测结果
Map<String, Double> probabilities = JSON.parseObject(simulationInfo.getResult(), new TypeReference<>() {
});
// id
Map<String, List<Long>> 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<String> getDistrictNames(List<RainfallDistrictSummaryResponseDTO> 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<String, Object> m,
XianInferenceResult simulationInfo,
List<XianHiddenDangerSpots> highHiddenDangerSpots,
List<XianHiddenDangerSpots> middleHiddenDangerSpots,
List<RainfallDistrictSummaryResponseDTO> rainData,
List<String> districtNames,
List<RiskAndHiddenSpotDTO> riskNumbers,
List<RiskAndHiddenSpotDTO> hiddenDangerNumbers,
Integer predictPointNum
) {
try {
BaseCondition condition = JSON.parseObject(simulationInfo.getCondition(), BaseCondition.class);
List<XianHiddenDangerSpots> 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<RainfallDistrictSummaryResponseDTO> rainData, List<String> districtNames) {
Map<String, String> 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<XianHiddenDangerSpots> hiddenDangerSpots) {
return hiddenDangerSpots.stream()
.map(XianHiddenDangerSpots::getDisasterType)
.filter(Objects::nonNull)
.distinct()
.map(name -> "暴雨-" + name)
.collect(Collectors.joining(""));
}
/**
* 获取暴雨灾害链位置
*
* @param hiddenDangerSpots 隐藏点信息
* @return 暴雨灾害链位置
*/
private String getRainfallDisasterChainPosition(List<XianHiddenDangerSpots> hiddenDangerSpots) {
return hiddenDangerSpots.stream()
.map(XianHiddenDangerSpots::getVillage)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.joining(""));
}
/**
* 获取次生灾害类型
*
* @param hiddenDangerSpots 隐藏点信息
* @return 次生灾害类型
*/
private String getSecondaryDisasterType(List<XianHiddenDangerSpots> hiddenDangerSpots) {
return hiddenDangerSpots.stream()
.map(XianHiddenDangerSpots::getDisasterType)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.joining(""));
}
}
@@ -19,7 +19,7 @@ import java.util.concurrent.Executor;
/**
* 初始化数据
*/
@Component
//@Component
@Slf4j
public class InitializeData {
@@ -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));
}
}
+10
View File
@@ -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
@@ -5,24 +5,29 @@
<mapper namespace="com.gis.xian.mapper.XianHiddenDangerSpotsMapper">
<resultMap id="XianHiddenDangerSpotsResultMap" type="com.gis.xian.entity.XianHiddenDangerSpots">
<id property="id" column="id" />
<result property="fieldCode" column="field_code" />
<result property="province" column="province" />
<result property="provinceId" column="province_id" />
<result property="city" column="city" />
<result property="cityId" column="city_id" />
<result property="county" column="county" />
<result property="countyId" column="county_id" />
<result property="village" column="village" />
<result property="disasterName" column="disaster_name" />
<result property="lon" column="lon" />
<result property="lat" column="lat" />
<result property="geom" column="geom" />
<result property="position" column="position" />
<result property="disasterType" column="disaster_type" />
<result property="scaleGrade" column="scale_grade" />
<result property="riskGrade" column="risk_grade" />
<result property="isDelete" column="is_delete" />
<id property="id" column="id"/>
<result property="fieldCode" column="field_code"/>
<result property="province" column="province"/>
<result property="provinceId" column="province_id"/>
<result property="city" column="city"/>
<result property="cityId" column="city_id"/>
<result property="county" column="county"/>
<result property="countyId" column="county_id"/>
<result property="village" column="village"/>
<result property="disasterName" column="disaster_name"/>
<result property="lon" column="lon"/>
<result property="lat" column="lat"/>
<result property="geom" column="geom"/>
<result property="position" column="position"/>
<result property="disasterType" column="disaster_type"/>
<result property="scaleGrade" column="scale_grade"/>
<result property="riskGrade" column="risk_grade"/>
<result property="isDelete" column="is_delete"/>
</resultMap>
<resultMap id="RiskAndHiddenSpotDTOMap" type="com.gis.xian.dto.RiskAndHiddenSpotDTO">
<result property="districtName" column="county"/>
<result property="number" column="number"/>
</resultMap>
<!-- 获取所有基础点:滑坡、崩塌、泥石流、山洪、内涝 -->
@@ -55,10 +60,42 @@
<!-- 根据id获取隐患点详情 -->
<select id="getPointDetailById" resultMap="XianHiddenDangerSpotsResultMap">
SELECT id, field_code, disaster_name, position, disaster_type, scale_grade, risk_grade FROM xian_hidden_danger_spots
SELECT id, field_code, disaster_name, position, disaster_type, scale_grade, risk_grade FROM
xian_hidden_danger_spots
<where>
id = #{id}
AND is_delete = 0
</where>
AND is_delete = 0
</select>
<select id="queryHiddenDangerNumberByDistrictName" resultMap="RiskAndHiddenSpotDTOMap">
SELECT county, COUNT(*) AS number FROM xian_risk_spots
<where>
is_delete = 0
<if test="districtNames != null and districtNames.size() > 0">
AND county IN(
<foreach collection="districtNames" item="districtName" separator=",">
#{districtName}
</foreach>
)
</if>
</where>
GROUP BY county
</select>
<select id="getPointDetailByIds" resultMap="XianHiddenDangerSpotsResultMap">
SELECT disaster_name, disaster_type, village, position, risk_grade FROM
xian_hidden_danger_spots
<where>
<if test="ids != null and ids.size() > 0">
id IN(
<foreach collection="ids" item="id" separator=",">
#{id}
</foreach>
)
</if>
AND is_delete = 0
</where>
</select>
</mapper>
@@ -5,16 +5,15 @@
<mapper namespace="com.gis.xian.mapper.XianInferenceResultMapper">
<resultMap id="XianInferenceResultResultMap" type="com.gis.xian.entity.XianInferenceResult">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="eventType" column="event_type" />
<result property="occurredTime" column="occurred_time" />
<result property="operationType" column="operation_type" />
<result property="result" column="result" />
<result property="condition" column="condition" />
<result property="filePath" column="file_path" />
<result property="createTime" column="create_time" />
<result property="isDelete" column="is_delete" />
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="eventType" column="event_type"/>
<result property="occurredTime" column="occurred_time"/>
<result property="operationType" column="operation_type"/>
<result property="result" column="result"/>
<result property="condition" column="condition"/>
<result property="createTime" column="create_time"/>
<result property="isDelete" column="is_delete"/>
</resultMap>
<!-- 根据id和pointId获取概率 -->
@@ -25,4 +24,28 @@
</where>
</select>
<select id="selectById" resultMap="XianInferenceResultResultMap">
SELECT
id, name, event_type, occurred_time, operation_type,
(
SELECT jsonb_object_agg(key, value)
FROM jsonb_each(result)
WHERE value::numeric >= 50
AND key LIKE '%\_1' ESCAPE '\'
) AS result
, condition
FROM xian_inference_result
<where>
id = #{id}
AND is_delete = 0
</where>
</select>
<select id="getTheNumberOfPredictedPoints" resultType="java.lang.Integer">
SELECT COUNT(*) FROM xian_inference_result
<where>
is_delete = 0
</where>
</select>
</mapper>
@@ -0,0 +1,29 @@
<?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="com.gis.xian.mapper.XianMeteorologyMapper">
<resultMap id="BaseResultMap" type="com.gis.xian.entity.XianMeteorology">
<id property="id" column="id" jdbcType="BIGINT"/>
<result property="stationName" column="station_name" jdbcType="VARCHAR"/>
<result property="visibility" column="visibility" jdbcType="DOUBLE"/>
<result property="areaCode" column="area_code" jdbcType="VARCHAR"/>
<result property="maxWindSpeed" column="max_wind_speed" jdbcType="DOUBLE"/>
<result property="rainfall1h" column="rainfall_1h" jdbcType="DOUBLE"/>
<result property="pressure" column="pressure" jdbcType="DOUBLE"/>
<result property="maxWindDirection" column="max_wind_direction" jdbcType="DOUBLE"/>
<result property="instMaxWindDirection" column="inst_max_wind_direction" jdbcType="DOUBLE"/>
<result property="stationId" column="station_id" jdbcType="VARCHAR"/>
<result property="temperature" column="temperature" jdbcType="DOUBLE"/>
<result property="relativeHumidity" column="relative_humidity" jdbcType="DOUBLE"/>
<result property="datetime" column="datetime" jdbcType="BIGINT"/>
<result property="lon" column="lon" jdbcType="DOUBLE"/>
<result property="lat" column="lat" jdbcType="DOUBLE"/>
<result property="geom" column="geom" jdbcType="OTHER"/>
<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
<result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
<result property="isDelete" column="is_delete" jdbcType="SMALLINT"/>
</resultMap>
</mapper>
@@ -5,28 +5,33 @@
<mapper namespace="com.gis.xian.mapper.XianRiskSpotsMapper">
<resultMap id="XianRiskSpotsResultMap" type="com.gis.xian.entity.XianRiskSpots">
<id property="id" column="id" />
<result property="riskName" column="risk_name" />
<result property="unitCode" column="unit_code" />
<result property="riskLevel" column="risk_level" />
<result property="area" column="area" />
<result property="province" column="province" />
<result property="city" column="city" />
<result property="county" column="county" />
<result property="country" column="country" />
<result property="village" column="village" />
<result property="position" column="position" />
<result property="residentCounts" column="resident_counts" />
<result property="addressPopulation" column="address_population" />
<result property="riskProperty" column="risk_property" />
<result property="permanentPopulation" column="permanent_population" />
<result property="housing" column="housing" />
<result property="inspectorName" column="inspector_name" />
<result property="inspectorTele" column="inspector_tele" />
<result property="lon" column="lon" />
<result property="lat" column="lat" />
<result property="geom" column="geom" />
<result property="isDelete" column="is_delete" />
<id property="id" column="id"/>
<result property="riskName" column="risk_name"/>
<result property="unitCode" column="unit_code"/>
<result property="riskLevel" column="risk_level"/>
<result property="area" column="area"/>
<result property="province" column="province"/>
<result property="city" column="city"/>
<result property="county" column="county"/>
<result property="country" column="country"/>
<result property="village" column="village"/>
<result property="position" column="position"/>
<result property="residentCounts" column="resident_counts"/>
<result property="addressPopulation" column="address_population"/>
<result property="riskProperty" column="risk_property"/>
<result property="permanentPopulation" column="permanent_population"/>
<result property="housing" column="housing"/>
<result property="inspectorName" column="inspector_name"/>
<result property="inspectorTele" column="inspector_tele"/>
<result property="lon" column="lon"/>
<result property="lat" column="lat"/>
<result property="geom" column="geom"/>
<result property="isDelete" column="is_delete"/>
</resultMap>
<resultMap id="RiskAndHiddenSpotDTOMap" type="com.gis.xian.dto.RiskAndHiddenSpotDTO">
<result property="districtName" column="county"/>
<result property="number" column="number"/>
</resultMap>
<!-- 获取所有风险点基础信息 -->
@@ -39,11 +44,26 @@
<!-- 根据id获取风险点信息 -->
<select id="getPointDetailById" resultMap="XianRiskSpotsResultMap">
SELECT id, risk_name, unit_code, position, resident_counts, risk_property, permanent_population, housing, inspector_name, inspector_tele, lon, lat FROM xian_risk_spots
SELECT id, risk_name, unit_code, position, resident_counts, risk_property, permanent_population, housing,
inspector_name, inspector_tele, lon, lat FROM xian_risk_spots
<where>
id = #{id} AND is_delete = 0
</where>
</select>
<select id="queryRiskNumberByDistrictName" resultMap="RiskAndHiddenSpotDTOMap">
SELECT county, COUNT(*) AS number FROM xian_risk_spots
<where>
is_delete = 0
<if test="districtNames != null and districtNames.size() > 0">
AND county IN(
<foreach collection="districtNames" item="districtName" separator=",">
#{districtName}
</foreach>
)
</if>
</where>
GROUP BY county
</select>
</mapper>
@@ -5,3 +5,5 @@ server:
algorithm:
server:
url: "http://localhost:8082"
connect-timeout: 5
read-timeout: 120
@@ -5,3 +5,5 @@ server:
algorithm:
server:
url: "http://localhost:8081"
connect-timeout: 5
read-timeout: 120