暴雨图层加载功能
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
package com.gis.xian.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 算法服务器配置属性
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Component
|
||||||
|
@ConfigurationProperties(prefix = "algorithm.server")
|
||||||
|
public class AlgorithmServerProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 算法服务器地址
|
||||||
|
*/
|
||||||
|
private String url;
|
||||||
|
}
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
package com.gis.xian.controller;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.gis.xian.config.AlgorithmServerProperties;
|
||||||
|
import com.gis.xian.domain.ApiResponse;
|
||||||
|
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")
|
||||||
|
@Slf4j
|
||||||
|
public class AlgorithmProxyController extends BaseController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private AlgorithmServerProperties algorithmServerProperties;
|
||||||
|
|
||||||
|
private final RestTemplate restTemplate = new RestTemplate();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理所有 GET 请求
|
||||||
|
*/
|
||||||
|
@GetMapping("/**")
|
||||||
|
public ApiResponse<Object> proxyGet(HttpServletRequest request) {
|
||||||
|
return proxyRequest(request, HttpMethod.GET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理所有 POST 请求
|
||||||
|
*/
|
||||||
|
@PostMapping("/**")
|
||||||
|
public ApiResponse<Object> proxyPost(HttpServletRequest request) throws IOException {
|
||||||
|
return proxyRequest(request, HttpMethod.POST);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理所有 PUT 请求
|
||||||
|
*/
|
||||||
|
@PutMapping("/**")
|
||||||
|
public ApiResponse<Object> proxyPut(HttpServletRequest request) throws IOException {
|
||||||
|
return proxyRequest(request, HttpMethod.PUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理所有 DELETE 请求
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/**")
|
||||||
|
public ApiResponse<Object> proxyDelete(HttpServletRequest request) {
|
||||||
|
return proxyRequest(request, HttpMethod.DELETE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理所有 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,7 +41,15 @@ safety:
|
|||||||
no-encrypt-paths:
|
no-encrypt-paths:
|
||||||
- /crypto/sm2/public-key
|
- /crypto/sm2/public-key
|
||||||
- /druid
|
- /druid
|
||||||
|
- /algorithm-api/rainfall/grid
|
||||||
# 请求无需解密的路径
|
# 请求无需解密的路径
|
||||||
no-decrypt-paths:
|
no-decrypt-paths:
|
||||||
- /crypto/sm2/public-key
|
- /crypto/sm2/public-key
|
||||||
- /druid
|
- /druid
|
||||||
|
- /algorithm-api/rainfall/grid
|
||||||
|
|
||||||
|
# 算法服务器配置
|
||||||
|
algorithm:
|
||||||
|
server:
|
||||||
|
# 开发环境算法服务器地址
|
||||||
|
url: http://localhost:8082
|
||||||
@@ -41,6 +41,14 @@ safety:
|
|||||||
# 响应无需加密的路径
|
# 响应无需加密的路径
|
||||||
no-encrypt-paths:
|
no-encrypt-paths:
|
||||||
- /crypto/sm2/public-key
|
- /crypto/sm2/public-key
|
||||||
|
- /algorithm-api/rainfall/grid
|
||||||
# 请求无需解密的路径
|
# 请求无需解密的路径
|
||||||
no-decrypt-paths:
|
no-decrypt-paths:
|
||||||
- /crypto/sm2/public-key
|
- /crypto/sm2/public-key
|
||||||
|
- /algorithm-api/rainfall/grid
|
||||||
|
|
||||||
|
# 算法服务器配置
|
||||||
|
algorithm:
|
||||||
|
server:
|
||||||
|
# 生产环境算法服务器地址
|
||||||
|
url: http://localhost:8081
|
||||||
Reference in New Issue
Block a user