修复数据总线访问日志无法显示状态码问题: http://172.16.46.63:31560/index.php?m=task&f=view&taskID=703. databus 新增 client 统一出口内容管理审计: http://172.16.46.63:31560/index.php?m=task&f=view&taskID=716
This commit is contained in:
82
zt-module-databus/zt-module-databus-client/pom.xml
Normal file
82
zt-module-databus/zt-module-databus-client/pom.xml
Normal file
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>zt-module-databus</artifactId>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<artifactId>zt-module-databus-server-client</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
Databus client, 提供调用第三方服务的能力并记录调用日志。
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.zt.plat</groupId>
|
||||
<artifactId>zt-module-databus-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,242 @@
|
||||
package com.zt.plat.module.databus.client;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.Method;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.zt.plat.framework.common.pojo.CommonResult;
|
||||
import com.zt.plat.framework.common.util.security.CryptoSignatureUtils;
|
||||
import com.zt.plat.module.databus.api.dto.ApiAccessLogCreateReq;
|
||||
import com.zt.plat.module.databus.api.provider.DatabusAccessLogProviderApi;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 数据总线提供的 http 客户端, 通过此客户端发起接口调用,会自动记录请求日志到数据总线
|
||||
* 2026/1/20 09:44
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DatabusClient {
|
||||
|
||||
/**
|
||||
* 多租户编号
|
||||
*/
|
||||
@Value("${zt.plat.databus.client.tenantId:1}")
|
||||
private Long tenantId;
|
||||
|
||||
@Resource
|
||||
private DatabusAccessLogProviderApi databusAccessLogProviderApi;
|
||||
|
||||
private static final int MAX_TEXT_LENGTH = 4000;
|
||||
|
||||
/**
|
||||
* 发送 get 请求
|
||||
* @param urlString 仅接口地址,不带参数,参数由data提供
|
||||
* @param data 请求参数
|
||||
* @param headers 请求头
|
||||
* @return 响应结果
|
||||
*/
|
||||
public String get(String urlString, Map<String, Object> data, Map<String, String> headers, String appId, String authToken) {
|
||||
return doRequest(urlString, data, headers, Method.GET, appId, authToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 post 请求
|
||||
* @param urlString 仅接口地址,不带参数,参数由data提供
|
||||
* @param data 请求数据
|
||||
* @param headers 请求头
|
||||
* @return 响应结果
|
||||
*/
|
||||
public String post(String urlString, Map<String, Object> data, Map<String, String> headers, String appId, String authToken) {
|
||||
return doRequest(urlString, data, headers, Method.POST, appId, authToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 put 请求
|
||||
* @param urlString 仅接口地址,不带参数,参数由data提供
|
||||
* @param data 请求数据
|
||||
* @param headers 请求头
|
||||
* @return 响应结果
|
||||
*/
|
||||
public String put(String urlString, Map<String, Object> data, Map<String, String> headers, String appId, String authToken) {
|
||||
return doRequest(urlString, data, headers, Method.PUT, appId, authToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送 delete 请求
|
||||
* @param urlString 仅接口地址,不带参数,参数由data提供
|
||||
* @param data 请求数据
|
||||
* @param headers 请求头
|
||||
* @return 响应结果
|
||||
*/
|
||||
public String delete(String urlString, Map<String, Object> data, Map<String, String> headers, String appId, String authToken) {
|
||||
return doRequest(urlString, data, headers, Method.DELETE, appId, authToken);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 发送请求到门户(token模式,不证书加密)
|
||||
* @param urlString 仅接口地址,不带参数,参数由data提供
|
||||
* @param data 请求数据
|
||||
* @param headers 请求头
|
||||
* @param method 请求方式,默认为 GET
|
||||
* @return 响应结果
|
||||
*/
|
||||
public String doRequest(String urlString, Map<String, Object> data, Map<String, String> headers, Method method, String appId, String authToken) {
|
||||
if (method == null) {
|
||||
method = Method.GET;
|
||||
}
|
||||
Assert.hasText(urlString, "接口地址不能为空");
|
||||
HttpRequest request;
|
||||
ApiAccessLogCreateReq logReq = new ApiAccessLogCreateReq();
|
||||
if (Method.GET.equals(method) || Method.DELETE.equals(method)) {
|
||||
logReq.setRequestQuery(JSONUtil.toJsonStr(data));
|
||||
} else {
|
||||
logReq.setRequestBody(JSONUtil.toJsonStr(data));
|
||||
}
|
||||
request = HttpUtil.createRequest(method, urlString).form(data);
|
||||
if (headers != null && !headers.isEmpty()) {
|
||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||
request.header(entry.getKey(), entry.getValue(), true);
|
||||
}
|
||||
}
|
||||
|
||||
logReq.setTenantId(tenantId);
|
||||
|
||||
logReq.setRequestMethod(method.name());
|
||||
logReq.setRequestPath(urlString);
|
||||
logReq.setCredentialAppId(appId);
|
||||
logReq.setRequestHeaders(JSONUtil.toJsonStr(headers));
|
||||
logReq.setUserAgent(request.header("User-Agent"));
|
||||
String result;
|
||||
logReq.setRequestTime(LocalDateTime.now());
|
||||
long requestTime = System.currentTimeMillis();
|
||||
try (HttpResponse response = request.execute()) {
|
||||
logReq.setDuration(System.currentTimeMillis() - requestTime);
|
||||
logReq.setResponseTime(LocalDateTime.now());
|
||||
result = response.body();
|
||||
logReq.setResponseStatus(response.getStatus());
|
||||
logReq.setResponseBody(result);
|
||||
logReq.setStatus(resolveStatus(response.getStatus()));
|
||||
Map<String, String> errorCodeAndMsg = extractErrorCodeAndMsg(result, response.getStatus());
|
||||
logReq.setErrorCode(errorCodeAndMsg.get("errorCode"));
|
||||
logReq.setErrorMessage(errorCodeAndMsg.get("errorMessage"));
|
||||
addAccessLog(logReq, appId, authToken);
|
||||
} catch (Exception e) {
|
||||
// 错误的日志服务端记录了,这里就不再记录了
|
||||
// logReq.setStatus(1);
|
||||
// logReq.setExceptionStack(buildStackTrace( e));
|
||||
// addAccessLog(logReq, appId, authToken);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addAccessLog(ApiAccessLogCreateReq logReq, String appId, String authToken) {
|
||||
String nonce = randomNonce();
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("tenant-id", String.valueOf(tenantId));
|
||||
headers.put("ZT-App-Id", appId);
|
||||
headers.put("ZT-Timestamp", Long.toString(System.currentTimeMillis()));
|
||||
headers.put("ZT-Nonce", nonce);
|
||||
headers.put("ZT-Auth-Token", authToken);
|
||||
CommonResult<Boolean> response = databusAccessLogProviderApi.add(headers, logReq);
|
||||
if (response.getCode() != 0) {
|
||||
throw new RuntimeException("添加访问日志失败: " + response);
|
||||
}
|
||||
}
|
||||
|
||||
private static String randomNonce() {
|
||||
return UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
|
||||
private String buildStackTrace(Throwable throwable) {
|
||||
try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
|
||||
throwable.printStackTrace(pw);
|
||||
return truncate(sw.toString());
|
||||
} catch (Exception ex) {
|
||||
return throwable.getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
private Integer resolveStatus(Integer httpStatus) {
|
||||
if (httpStatus == null) {
|
||||
return 3;
|
||||
}
|
||||
if (httpStatus >= 200 && httpStatus < 400) {
|
||||
return 0;
|
||||
}
|
||||
if (httpStatus >= 400 && httpStatus < 500) {
|
||||
return 1;
|
||||
}
|
||||
if (httpStatus >= 500) {
|
||||
return 2;
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
private Map<String, String> extractErrorCodeAndMsg(String responseBody, Integer responseStatus) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
if (!isErrorStatus(responseStatus)) {
|
||||
return result;
|
||||
}
|
||||
if (JSONUtil.isTypeJSONObject(responseBody)) {
|
||||
JSONObject map = JSONUtil.parseObj(responseBody);
|
||||
Object errorCode = firstNonNull(map.get("errorCode"), map.get("code"));
|
||||
errorCode = errorCode == null ? null : truncate(String.valueOf(errorCode));
|
||||
if (errorCode != null) {
|
||||
result.put("errorCode", errorCode.toString());
|
||||
}
|
||||
Object message = firstNonNull(map.get("errorMessage"), map.get("message"));
|
||||
if (message != null) {
|
||||
message = truncate(String.valueOf(message));
|
||||
result.put("errorMessage", message.toString());
|
||||
}
|
||||
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private boolean isErrorStatus(Integer responseStatus) {
|
||||
return responseStatus != null && responseStatus >= 400;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
private <T> T firstNonNull(T... candidates) {
|
||||
if (candidates == null) {
|
||||
return null;
|
||||
}
|
||||
for (T candidate : candidates) {
|
||||
if (candidate != null) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String truncate(String text) {
|
||||
if (!StringUtils.hasText(text)) {
|
||||
return text;
|
||||
}
|
||||
if (text.length() <= MAX_TEXT_LENGTH) {
|
||||
return text;
|
||||
}
|
||||
return text.substring(0, MAX_TEXT_LENGTH);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.zt.plat.module.databus.client;
|
||||
|
||||
/**
|
||||
*
|
||||
* 2026/1/21 10:48
|
||||
*/
|
||||
|
||||
import com.zt.plat.module.databus.api.provider.DatabusAccessLogProviderApi;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableFeignClients(clients = {DatabusAccessLogProviderApi.class})
|
||||
public class RpcConfiguration {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.zt.plat.module.databus.client.DatabusClient,\
|
||||
com.zt.plat.module.databus.client.RpcConfiguration
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.zt.plat.module.databus;
|
||||
|
||||
import com.zt.plat.module.databus.client.DatabusClient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
*
|
||||
* 2026/1/20 14:29
|
||||
*/
|
||||
@SpringBootTest(classes = TestApplication.class)
|
||||
public class DatabusClientTest {
|
||||
|
||||
@Autowired
|
||||
private DatabusClient databusClient;
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
String result = databusClient.get("https://www.baidu.com/", null, null, "jwyw2", "a5d7cf609c0b47038ea405c660726ee9");
|
||||
System.out.println(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.zt.plat.module.databus;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
/**
|
||||
*
|
||||
* 2026/1/20 14:26
|
||||
*/
|
||||
@SpringBootTest
|
||||
@SpringBootApplication(exclude = {
|
||||
DataSourceAutoConfiguration.class,
|
||||
DataSourceTransactionManagerAutoConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class,
|
||||
JdbcTemplateAutoConfiguration.class,
|
||||
})
|
||||
public class TestApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TestApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: 172.16.46.63:30848 # Nacos 服务器地址
|
||||
username: nacos # Nacos 账号
|
||||
password: P@ssword25 # Nacos 密码
|
||||
discovery: # 【配置中心】配置项
|
||||
namespace: klw # 命名空间。这里使用 maven Profile 资源过滤进行动态替换
|
||||
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
|
||||
metadata:
|
||||
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
|
||||
Reference in New Issue
Block a user