Files
ccdi/ccdi-lsfx/src/main/java/com/ruoyi/lsfx/util/HttpUtil.java

316 lines
13 KiB
Java
Raw Normal View History

2026-03-02 09:59:10 +08:00
package com.ruoyi.lsfx.util;
import com.fasterxml.jackson.databind.ObjectMapper;
2026-07-20 15:35:54 +08:00
import com.ruoyi.lsfx.domain.CallerContext;
2026-03-05 15:01:33 +08:00
import com.ruoyi.lsfx.exception.LsfxApiException;
2026-07-20 15:35:54 +08:00
import com.ruoyi.system.domain.SysApiLog;
import com.ruoyi.system.service.ISysApiLogService;
2026-03-03 14:51:46 +08:00
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
2026-03-05 15:01:33 +08:00
import org.springframework.core.io.FileSystemResource;
2026-07-20 15:35:54 +08:00
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.MediaTypeFactory;
import org.springframework.http.ResponseEntity;
2026-03-02 09:59:10 +08:00
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestClientException;
2026-07-20 15:35:54 +08:00
import org.springframework.web.client.RestClientResponseException;
2026-03-02 09:59:10 +08:00
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
2026-03-02 09:59:10 +08:00
2026-03-05 15:01:33 +08:00
import java.io.File;
2026-07-20 15:35:54 +08:00
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
2026-03-02 09:59:10 +08:00
import java.util.Map;
/**
2026-07-20 15:35:54 +08:00
* 业务外部HTTP请求工具统一保存完整调用日志
2026-03-02 09:59:10 +08:00
*/
@Component
public class HttpUtil {
private static final Logger log = LoggerFactory.getLogger(HttpUtil.class);
2026-07-20 15:35:54 +08:00
private static final String CALL_SUCCESS = "0";
private static final String CALL_FAILED = "1";
2026-03-02 09:59:10 +08:00
@Resource
private RestTemplate restTemplate;
@Resource
private ObjectMapper objectMapper;
2026-07-20 15:35:54 +08:00
@Resource
private ISysApiLogService apiLogService;
public static org.springframework.core.io.Resource namedFileResource(File file, String filename) {
return new NamedFileSystemResource(file, filename);
}
private static class NamedFileSystemResource extends FileSystemResource {
private final String filename;
NamedFileSystemResource(File file, String filename) {
super(file);
this.filename = StringUtils.hasText(filename) ? filename : file.getName();
}
@Override
public String getFilename() {
return filename;
}
}
2026-07-20 15:35:54 +08:00
public <T> T get(CallerContext caller, String url, Map<String, Object> params,
Map<String, String> headers, Class<T> responseType) {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
if (params != null) {
params.forEach((key, value) -> {
if (value != null) {
builder.queryParam(key, value);
}
});
}
2026-07-20 15:35:54 +08:00
String fullUrl = builder.toUriString();
HttpHeaders httpHeaders = createHeaders(headers);
return execute(caller, fullUrl, HttpMethod.GET, httpHeaders, params,
new HttpEntity<>(httpHeaders), responseType, "API返回数据为空", "GET请求异常");
}
2026-07-20 15:35:54 +08:00
public <T> T get(CallerContext caller, String url, Map<String, String> headers, Class<T> responseType) {
HttpHeaders httpHeaders = createHeaders(headers);
return execute(caller, url, HttpMethod.GET, httpHeaders, null,
new HttpEntity<>(httpHeaders), responseType, "API返回数据为空", "网络请求失败");
}
2026-07-20 15:35:54 +08:00
public <T> T postJson(CallerContext caller, String url, Object request,
Map<String, String> headers, Class<T> responseType) {
HttpHeaders httpHeaders = createHeaders(headers);
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
return execute(caller, url, HttpMethod.POST, httpHeaders, request,
new HttpEntity<>(request, httpHeaders), responseType, "API返回数据为空", "网络请求失败");
}
2026-07-20 15:35:54 +08:00
public <T> T postFormData(CallerContext caller, String url, Map<String, Object> params,
Map<String, String> headers, Class<T> responseType) {
HttpHeaders httpHeaders = createHeaders(headers);
httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
if (params != null) {
params.forEach(body::add);
}
2026-07-20 15:35:54 +08:00
return execute(caller, url, HttpMethod.POST, httpHeaders, params,
new HttpEntity<>(body, httpHeaders), responseType, "API返回数据为空", "网络请求失败");
2026-03-02 09:59:10 +08:00
}
2026-07-20 15:35:54 +08:00
public <T> T postUrlEncodedForm(CallerContext caller, String url, Map<String, Object> params,
Map<String, String> headers, Class<T> responseType) {
HttpHeaders httpHeaders = createHeaders(headers);
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> body = toUrlEncodedBody(params);
return execute(caller, url, HttpMethod.POST, httpHeaders, params,
new HttpEntity<>(body, httpHeaders), responseType, "API返回数据为空", "网络请求失败");
}
2026-03-02 09:59:10 +08:00
2026-07-20 15:35:54 +08:00
public String postUrlEncodedFormForString(CallerContext caller, String url,
Map<String, Object> params, Map<String, String> headers) {
HttpHeaders httpHeaders = createHeaders(headers);
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> body = toUrlEncodedBody(params);
return execute(caller, url, HttpMethod.POST, httpHeaders, params,
new HttpEntity<>(body, httpHeaders), String.class, "API返回数据为空", "网络请求失败");
}
2026-03-02 09:59:10 +08:00
2026-07-20 15:35:54 +08:00
public <T> T uploadFile(CallerContext caller, String url, Map<String, Object> params,
Map<String, String> headers, Class<T> responseType) {
HttpHeaders httpHeaders = createHeaders(headers);
httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
if (params != null) {
params.forEach((key, value) -> {
if (value instanceof File file) {
body.add(key, new FileSystemResource(file));
} else {
body.add(key, value);
}
});
}
2026-07-20 15:35:54 +08:00
return execute(caller, url, HttpMethod.POST, httpHeaders, params,
new HttpEntity<>(body, httpHeaders), responseType, "文件上传返回数据为空", "文件上传请求失败");
2026-03-02 09:59:10 +08:00
}
2026-07-20 15:35:54 +08:00
private <T> T execute(CallerContext caller, String url, HttpMethod method, HttpHeaders requestHeaders,
Object requestParams, HttpEntity<?> requestEntity, Class<T> responseType,
String emptyResponseMessage, String requestFailureMessage) {
if (caller == null) {
throw new IllegalArgumentException("接口调用用户不能为空");
2026-03-03 14:51:46 +08:00
}
2026-07-20 15:35:54 +08:00
long startTime = System.currentTimeMillis();
SysApiLog apiLog = createBaseApiLog(caller, url, method, startTime);
try {
2026-07-20 15:35:54 +08:00
captureRequest(apiLog, requestHeaders, requestParams);
} catch (Exception e) {
log.error("接口日志请求快照构建失败: method={}, url={}", method, url, e);
}
try {
ResponseEntity<String> response = restTemplate.exchange(url, method, requestEntity, String.class);
captureResponse(apiLog, response);
if (!response.getStatusCode().is2xxSuccessful()) {
2026-07-20 15:35:54 +08:00
throw new LsfxApiException("API调用失败HTTP状态码: " + response.getStatusCode().value());
}
2026-07-20 15:35:54 +08:00
if (!StringUtils.hasText(response.getBody())) {
throw new LsfxApiException(emptyResponseMessage);
}
2026-07-20 15:35:54 +08:00
T result = parseResponse(response.getBody(), responseType);
apiLog.setCallStatus(CALL_SUCCESS);
return result;
} catch (RestClientResponseException e) {
apiLog.setResponseStatus(e.getStatusCode().value());
apiLog.setResponseHeaders(safeSerialize(e.getResponseHeaders()));
apiLog.setResponseBody(e.getResponseBodyAsString());
apiLog.setErrorMsg(formatException(e));
throw new LsfxApiException(requestFailureMessage + ": " + e.getMessage(), e);
} catch (LsfxApiException e) {
apiLog.setErrorMsg(formatException(e));
throw e;
} catch (RestClientException e) {
2026-07-20 15:35:54 +08:00
apiLog.setErrorMsg(formatException(e));
throw new LsfxApiException(requestFailureMessage + ": " + e.getMessage(), e);
} catch (Exception e) {
apiLog.setErrorMsg(formatException(e));
throw new LsfxApiException("响应解析失败: " + e.getMessage(), e);
} finally {
apiLog.setCostTime(System.currentTimeMillis() - startTime);
persistApiLog(apiLog);
}
}
2026-07-20 15:35:54 +08:00
private SysApiLog createBaseApiLog(CallerContext caller, String url, HttpMethod method, long startTime) {
SysApiLog apiLog = new SysApiLog();
apiLog.setCallerUserId(caller.userId());
apiLog.setCallerUsername(caller.username());
apiLog.setApiUrl(url);
apiLog.setHttpMethod(method.name());
apiLog.setCallStatus(CALL_FAILED);
apiLog.setCallTime(new Date(startTime));
return apiLog;
}
2026-07-20 15:35:54 +08:00
private void captureRequest(SysApiLog apiLog, HttpHeaders headers, Object params) {
apiLog.setContentType(headers.getContentType() == null ? null : headers.getContentType().toString());
apiLog.setRequestHeaders(safeSerialize(headers));
apiLog.setRequestParams(safeSerialize(normalizeValue(params)));
}
2026-07-20 15:35:54 +08:00
private void captureResponse(SysApiLog apiLog, ResponseEntity<String> response) {
apiLog.setResponseStatus(response.getStatusCode().value());
apiLog.setResponseHeaders(safeSerialize(response.getHeaders()));
apiLog.setResponseBody(response.getBody());
}
2026-07-20 15:35:54 +08:00
@SuppressWarnings("unchecked")
private <T> T parseResponse(String responseBody, Class<T> responseType) throws Exception {
if (String.class.equals(responseType)) {
return (T) responseBody;
}
2026-07-20 15:35:54 +08:00
return objectMapper.readValue(responseBody, responseType);
}
2026-07-20 15:35:54 +08:00
private MultiValueMap<String, String> toUrlEncodedBody(Map<String, Object> params) {
MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
if (params != null) {
params.forEach((key, value) -> {
if (value != null) {
body.add(key, value.toString());
}
});
}
return body;
}
2026-03-02 09:59:10 +08:00
2026-07-20 15:35:54 +08:00
private HttpHeaders createHeaders(Map<String, String> headers) {
HttpHeaders httpHeaders = new HttpHeaders();
if (headers != null) {
headers.forEach(httpHeaders::set);
}
return httpHeaders;
}
2026-03-02 09:59:10 +08:00
2026-07-20 15:35:54 +08:00
private Object normalizeValue(Object value) {
if (value == null) {
return null;
}
if (value instanceof File file) {
return fileMetadata(file.getName(), file.length());
}
if (value instanceof FileSystemResource resource) {
return fileMetadata(resource.getFilename(), resource.getFile().length());
}
if (value instanceof org.springframework.core.io.Resource resource) {
return fileMetadata(resource.getFilename(), null);
}
if (value instanceof Map<?, ?> map) {
Map<String, Object> normalized = new LinkedHashMap<>();
map.forEach((key, item) -> normalized.put(String.valueOf(key), normalizeValue(item)));
return normalized;
}
if (value instanceof Iterable<?> iterable) {
List<Object> normalized = new ArrayList<>();
iterable.forEach(item -> normalized.add(normalizeValue(item)));
return normalized;
}
if (value.getClass().isArray()) {
List<Object> normalized = new ArrayList<>();
for (int i = 0; i < Array.getLength(value); i++) {
normalized.add(normalizeValue(Array.get(value, i)));
}
2026-07-20 15:35:54 +08:00
return normalized;
}
return value;
}
2026-07-20 15:35:54 +08:00
private Map<String, Object> fileMetadata(String filename, Long size) {
Map<String, Object> metadata = new LinkedHashMap<>();
metadata.put("filename", filename);
metadata.put("size", size);
metadata.put("contentType", MediaTypeFactory.getMediaType(filename == null ? "" : filename)
.map(MediaType::toString).orElse(null));
return metadata;
}
2026-07-20 15:35:54 +08:00
private String safeSerialize(Object value) {
try {
return objectMapper.writeValueAsString(value);
} catch (Exception e) {
log.error("接口日志序列化失败", e);
return String.valueOf(value);
}
2026-03-02 09:59:10 +08:00
}
2026-07-20 15:35:54 +08:00
private String formatException(Exception exception) {
StringWriter writer = new StringWriter();
exception.printStackTrace(new PrintWriter(writer));
return writer.toString();
}
private void persistApiLog(SysApiLog apiLog) {
try {
apiLogService.recordApiLog(apiLog);
} catch (Exception e) {
log.error("接口日志入库失败: method={}, url={}", apiLog.getHttpMethod(), apiLog.getApiUrl(), e);
2026-03-02 09:59:10 +08:00
}
}
}