From 413ce4c1ef32295838a6b070d0408c06eaf7de2d Mon Sep 17 00:00:00 2001
From: ranke <213539@qq.com>
Date: Thu, 22 Jan 2026 09:35:13 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=95=B0=E6=8D=AE=E6=80=BB?=
=?UTF-8?q?=E7=BA=BF=E8=AE=BF=E9=97=AE=E6=97=A5=E5=BF=97=E6=97=A0=E6=B3=95?=
=?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=8A=B6=E6=80=81=E7=A0=81=E9=97=AE=E9=A2=98?=
=?UTF-8?q?:=20http://172.16.46.63:31560/index.php=3Fm=3Dtask&f=3Dview&tas?=
=?UTF-8?q?kID=3D703.=20=20databus=20=20=E6=96=B0=E5=A2=9E=20client=20?=
=?UTF-8?q?=E7=BB=9F=E4=B8=80=E5=87=BA=E5=8F=A3=E5=86=85=E5=AE=B9=E7=AE=A1?=
=?UTF-8?q?=E7=90=86=E5=AE=A1=E8=AE=A1:=20http://172.16.46.63:31560/index.?=
=?UTF-8?q?php=3Fm=3Dtask&f=3Dview&taskID=3D716?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 1 +
zt-dependencies/pom.xml | 2 +-
zt-module-databus/pom.xml | 1 +
.../api/dto/ApiAccessLogCreateReq.java | 152 +++++++++++
.../provider/DatabusAccessLogProviderApi.java | 28 ++
.../zt-module-databus-client/pom.xml | 82 ++++++
.../module/databus/client/DatabusClient.java | 242 ++++++++++++++++++
.../databus/client/RpcConfiguration.java | 16 ++
.../main/resources/META-INF/spring.factories | 3 +
.../module/databus/DatabusClientTest.java | 24 ++
.../plat/module/databus/TestApplication.java | 26 ++
.../src/test/resources/application.yaml | 11 +
.../api/DatabusAccessLogProviderApiImpl.java | 45 ++++
.../gateway/core/ApiGatewayAccessLogger.java | 1 +
.../core/ApiGatewayExecutionService.java | 1 +
.../security/GatewaySecurityFilter.java | 37 ++-
16 files changed, 659 insertions(+), 13 deletions(-)
create mode 100644 zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/dto/ApiAccessLogCreateReq.java
create mode 100644 zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/provider/DatabusAccessLogProviderApi.java
create mode 100644 zt-module-databus/zt-module-databus-client/pom.xml
create mode 100644 zt-module-databus/zt-module-databus-client/src/main/java/com/zt/plat/module/databus/client/DatabusClient.java
create mode 100644 zt-module-databus/zt-module-databus-client/src/main/java/com/zt/plat/module/databus/client/RpcConfiguration.java
create mode 100644 zt-module-databus/zt-module-databus-client/src/main/resources/META-INF/spring.factories
create mode 100644 zt-module-databus/zt-module-databus-client/src/test/java/com/zt/plat/module/databus/DatabusClientTest.java
create mode 100644 zt-module-databus/zt-module-databus-client/src/test/java/com/zt/plat/module/databus/TestApplication.java
create mode 100644 zt-module-databus/zt-module-databus-client/src/test/resources/application.yaml
create mode 100644 zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/api/DatabusAccessLogProviderApiImpl.java
diff --git a/.gitignore b/.gitignore
index e55eb64b..3d91d080 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,6 +61,7 @@ package-lock.json
# visual studio code
.history
*.log
+logs/**
functions/mock
.temp/**
diff --git a/zt-dependencies/pom.xml b/zt-dependencies/pom.xml
index ceb1881b..69fbe2fd 100644
--- a/zt-dependencies/pom.xml
+++ b/zt-dependencies/pom.xml
@@ -72,7 +72,7 @@
1.18.1
1.18.36
1.6.3
- 5.8.35
+ 5.8.43
6.0.0-M19
4.0.3
2.4.1
diff --git a/zt-module-databus/pom.xml b/zt-module-databus/pom.xml
index 904627a5..c06c914b 100644
--- a/zt-module-databus/pom.xml
+++ b/zt-module-databus/pom.xml
@@ -11,6 +11,7 @@
zt-module-databus-api
zt-module-databus-server
zt-module-databus-server-app
+ zt-module-databus-client
4.0.0
diff --git a/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/dto/ApiAccessLogCreateReq.java b/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/dto/ApiAccessLogCreateReq.java
new file mode 100644
index 00000000..fb3536bb
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/dto/ApiAccessLogCreateReq.java
@@ -0,0 +1,152 @@
+package com.zt.plat.module.databus.api.dto;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.zt.plat.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * 新增Databus API 访问日志请求体。
+ */
+@Data
+@EqualsAndHashCode
+public class ApiAccessLogCreateReq {
+
+ /**
+ * 主键
+ */
+ @Schema(description = "主键")
+ private Long id;
+
+ /**
+ * HTTP 方法
+ */
+ @Schema(description = "HTTP 方法")
+ @NotNull(message = "HTTP 方法不能为空")
+ private String requestMethod;
+
+ /**
+ * 请求路径
+ */
+ @Schema(description = "请求路径")
+ @NotNull(message = "请求路径不能为空")
+ private String requestPath;
+
+ /**
+ * 调用使用的应用标识
+ */
+ @Schema(description = "调用使用的应用标识")
+ @NotNull(message = "调用使用的应用标识不能为空")
+ private String credentialAppId;
+
+ /**
+ * 多租户编号
+ */
+ @Schema(description = "多租户编号")
+ @NotNull(message = "多租户编号不能为空")
+ private Long tenantId;
+
+ /**
+ * 查询参数(JSON 字符串)
+ */
+ @Schema(description = "查询参数(JSON 字符串)")
+ private String requestQuery;
+
+ /**
+ * 请求头信息(JSON 字符串)
+ */
+ @Schema(description = "请求头信息(JSON 字符串)")
+ private String requestHeaders;
+
+ /**
+ * 请求体(JSON 字符串)
+ */
+ @Schema(description = "请求体(JSON 字符串)")
+ private String requestBody;
+
+ /**
+ * 响应 HTTP 状态码
+ */
+ @Schema(description = "响应 HTTP 状态码")
+ private Integer responseStatus;
+
+ /**
+ * 响应提示信息
+ */
+ @Schema(description = "响应提示信息")
+ private String responseMessage;
+
+ /**
+ * 响应体(JSON 字符串)
+ */
+ @Schema(description = "响应体(JSON 字符串)")
+ private String responseBody;
+
+ /**
+ * 访问状态:0-成功 1-客户端错误 2-服务端错误 3-未知
+ */
+ @Schema(description = "访问状态:0-成功 1-客户端错误 2-服务端错误 3-未知")
+ @NotNull(message = "访问状态不能为空")
+ private Integer status;
+
+ /**
+ * 业务错误码
+ */
+ @Schema(description = "业务错误码")
+ private String errorCode;
+
+ /**
+ * 错误信息
+ */
+ @Schema(description = "错误信息")
+ private String errorMessage;
+
+ /**
+ * 异常堆栈
+ */
+ @Schema(description = "异常堆栈")
+ private String exceptionStack;
+
+ /**
+ * 客户端 IP
+ */
+ @Schema(description = "客户端 IP")
+ private String clientIp;
+
+ /**
+ * User-Agent
+ */
+ @Schema(description = "User-Agent")
+ private String userAgent;
+
+ /**
+ * 请求耗时(毫秒)
+ */
+ @Schema(description = "请求耗时(毫秒)")
+ private Long duration;
+
+ /**
+ * 请求时间
+ */
+ @Schema(description = "请求时间")
+ @JsonSerialize(using = TimestampLocalDateTimeSerializer.class)
+ private LocalDateTime requestTime;
+
+ /**
+ * 响应时间
+ */
+ @Schema(description = "响应时间")
+ @JsonSerialize(using = TimestampLocalDateTimeSerializer.class)
+ private LocalDateTime responseTime;
+
+ /**
+ * 额外调试信息(JSON 字符串)
+ */
+ @Schema(description = "额外调试信息(JSON 字符串)")
+ private String extra;
+
+}
diff --git a/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/provider/DatabusAccessLogProviderApi.java b/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/provider/DatabusAccessLogProviderApi.java
new file mode 100644
index 00000000..a2f80c6e
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-api/src/main/java/com/zt/plat/module/databus/api/provider/DatabusAccessLogProviderApi.java
@@ -0,0 +1,28 @@
+package com.zt.plat.module.databus.api.provider;
+
+import com.zt.plat.framework.common.pojo.CommonResult;
+import com.zt.plat.module.databus.api.dto.ApiAccessLogCreateReq;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+
+import java.util.Map;
+
+/**
+ * Databus API访问日志接口
+ * 2026/1/20 16:26
+ */
+@FeignClient(name = "${databus.provider.log.service:databus-server}")
+@Tag(name = "RPC 服务 - Databus API访问日志接口")
+public interface DatabusAccessLogProviderApi {
+
+ String PREFIX = "/databus/api/portal/access-log";
+
+ @PostMapping(PREFIX + "/add")
+ @Operation(summary = "新增访问日志")
+ CommonResult add(@RequestHeader Map headers, @RequestBody ApiAccessLogCreateReq req);
+
+}
diff --git a/zt-module-databus/zt-module-databus-client/pom.xml b/zt-module-databus/zt-module-databus-client/pom.xml
new file mode 100644
index 00000000..a8abc82f
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-client/pom.xml
@@ -0,0 +1,82 @@
+
+
+ 4.0.0
+
+ zt-module-databus
+ com.zt.plat
+ ${revision}
+
+ zt-module-databus-server-client
+ jar
+ ${project.artifactId}
+
+ Databus client, 提供调用第三方服务的能力并记录调用日志。
+
+
+
+
+
+
+ com.zt.plat
+ zt-module-databus-api
+ ${revision}
+
+
+
+ cn.hutool
+ hutool-all
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+ provided
+
+
+
+ org.apache.commons
+ commons-lang3
+
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-loadbalancer
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+
diff --git a/zt-module-databus/zt-module-databus-client/src/main/java/com/zt/plat/module/databus/client/DatabusClient.java b/zt-module-databus/zt-module-databus-client/src/main/java/com/zt/plat/module/databus/client/DatabusClient.java
new file mode 100644
index 00000000..ad921ceb
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-client/src/main/java/com/zt/plat/module/databus/client/DatabusClient.java
@@ -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 data, Map 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 data, Map 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 data, Map 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 data, Map 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 data, Map 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 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 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 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 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 extractErrorCodeAndMsg(String responseBody, Integer responseStatus) {
+ Map 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 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);
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-client/src/main/java/com/zt/plat/module/databus/client/RpcConfiguration.java b/zt-module-databus/zt-module-databus-client/src/main/java/com/zt/plat/module/databus/client/RpcConfiguration.java
new file mode 100644
index 00000000..08c69d96
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-client/src/main/java/com/zt/plat/module/databus/client/RpcConfiguration.java
@@ -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 {
+
+}
diff --git a/zt-module-databus/zt-module-databus-client/src/main/resources/META-INF/spring.factories b/zt-module-databus/zt-module-databus-client/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000..5fe820ee
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-client/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,3 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.zt.plat.module.databus.client.DatabusClient,\
+com.zt.plat.module.databus.client.RpcConfiguration
\ No newline at end of file
diff --git a/zt-module-databus/zt-module-databus-client/src/test/java/com/zt/plat/module/databus/DatabusClientTest.java b/zt-module-databus/zt-module-databus-client/src/test/java/com/zt/plat/module/databus/DatabusClientTest.java
new file mode 100644
index 00000000..b26c74d8
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-client/src/test/java/com/zt/plat/module/databus/DatabusClientTest.java
@@ -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);
+ }
+
+}
diff --git a/zt-module-databus/zt-module-databus-client/src/test/java/com/zt/plat/module/databus/TestApplication.java b/zt-module-databus/zt-module-databus-client/src/test/java/com/zt/plat/module/databus/TestApplication.java
new file mode 100644
index 00000000..fa8ca7d9
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-client/src/test/java/com/zt/plat/module/databus/TestApplication.java
@@ -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);
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-client/src/test/resources/application.yaml b/zt-module-databus/zt-module-databus-client/src/test/resources/application.yaml
new file mode 100644
index 00000000..a47ddc71
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-client/src/test/resources/application.yaml
@@ -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 # 服务实例的版本号,可用于灰度发布
\ No newline at end of file
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/api/DatabusAccessLogProviderApiImpl.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/api/DatabusAccessLogProviderApiImpl.java
new file mode 100644
index 00000000..a700c240
--- /dev/null
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/api/DatabusAccessLogProviderApiImpl.java
@@ -0,0 +1,45 @@
+package com.zt.plat.module.databus.api;
+
+import com.zt.plat.framework.common.pojo.CommonResult;
+import com.zt.plat.framework.common.util.monitor.TracerUtils;
+import com.zt.plat.framework.common.util.object.BeanUtils;
+import com.zt.plat.framework.common.util.servlet.ServletUtils;
+import com.zt.plat.module.databus.api.dto.ApiAccessLogCreateReq;
+import com.zt.plat.module.databus.api.provider.DatabusAccessLogProviderApi;
+import com.zt.plat.module.databus.dal.dataobject.gateway.ApiAccessLogDO;
+import com.zt.plat.module.databus.service.gateway.ApiAccessLogService;
+import jakarta.annotation.Resource;
+import jakarta.annotation.security.PermitAll;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.LocalDateTime;
+import java.util.Map;
+
+import static com.zt.plat.framework.common.pojo.CommonResult.success;
+
+/**
+ *
+ * 2026/1/21 10:05
+ */
+@RestController
+public class DatabusAccessLogProviderApiImpl implements DatabusAccessLogProviderApi {
+
+ @Resource
+ private ApiAccessLogService apiAccessLogService;
+
+ @Override
+ @PermitAll
+ public CommonResult add(Map headers, ApiAccessLogCreateReq req) {
+ ApiAccessLogDO logDO = new ApiAccessLogDO();
+ BeanUtils.copyProperties(req, logDO);
+ logDO.setTraceId(TracerUtils.getTraceId());
+ logDO.setClientIp(ServletUtils.getClientIP());
+ logDO.setCreateTime(LocalDateTime.now());
+ logDO.setUpdateTime(LocalDateTime.now());
+ logDO.setCreator("1");
+ logDO.setUpdater("1");
+
+ apiAccessLogService.create(logDO);
+ return success(Boolean.TRUE);
+ }
+}
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayAccessLogger.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayAccessLogger.java
index 741db3f2..d56e56a6 100644
--- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayAccessLogger.java
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayAccessLogger.java
@@ -159,6 +159,7 @@ public class ApiGatewayAccessLogger {
update.setStatus(resolveStatus(status));
update.setResponseTime(LocalDateTime.now());
update.setDuration(calculateDuration(request));
+ update.setUpdater("1");
apiAccessLogService.update(update);
} catch (Exception ex) {
log.warn("更新入口 API 访问日志失败, logId={}", logId, ex);
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayExecutionService.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayExecutionService.java
index 14a5ed11..6804d03b 100644
--- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayExecutionService.java
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/core/ApiGatewayExecutionService.java
@@ -89,6 +89,7 @@ public class ApiGatewayExecutionService {
} else {
responseContext = apiFlowDispatcher.dispatch(context.getApiCode(), context.getApiVersion(), context);
}
+ responseContext.setResponseStatus(resolveStatus(responseContext));
} catch (ServiceException ex) {
errorProcessor.applyServiceException(context, ex);
accessLogger.onException(context, ex);
diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java
index 00025b83..d0839a7d 100644
--- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java
+++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java
@@ -77,6 +77,9 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
filterChain.doFilter(request, response);
return;
}
+
+ // 添加日志接口需要特殊处理
+ boolean isNormalProcess = !pathMatcher.match("/databus/api/portal/access-log/**", pathWithinApplication);
Long accessLogId = null;
ApiGatewayProperties.Security security = properties.getSecurity();
ApiClientCredentialDO credential = null;
@@ -95,8 +98,10 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
accessLogger.finalizeEarly(request, HttpStatus.FORBIDDEN.value(), "IP 禁止访问");
return;
}
- // IP 校验通过后再补录入口日志,避免无租户信息写库
- accessLogId = accessLogger.logEntrance(request);
+ // IP 校验通过后再补录入口日志,避免无租户信息写库, 非日志添加接口才记录日志
+ if (isNormalProcess) {
+ accessLogId = accessLogger.logEntrance(request);
+ }
if (!security.isEnabled()) {
byte[] originalBody = StreamUtils.copyToByteArray(request.getInputStream());
CachedBodyHttpServletRequest passthroughRequest = new CachedBodyHttpServletRequest(request, originalBody);
@@ -128,13 +133,17 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
if (nonce.length() < 8) {
throw new SecurityValidationException(HttpStatus.BAD_REQUEST, "随机数长度不足");
}
- String signature = requireHeader(request, SIGNATURE_HEADER, "缺少签名");
- // 尝试按凭证配置解密请求体,并构建签名载荷进行校验
- byte[] decryptedBody = decryptRequestBody(requestBody, credential, security);
- verifySignature(request, decryptedBody, signature, credential, security, appId, timestampHeader);
+ if (isNormalProcess) {
+ // 非日志添加接口才处理
+ String signature = requireHeader(request, SIGNATURE_HEADER, "缺少签名");
+ // 尝试按凭证配置解密请求体,并构建签名载荷进行校验
+ byte[] decryptedBody = decryptRequestBody(requestBody, credential, security);
+ verifySignature(request, decryptedBody, signature, credential, security, appId, timestampHeader);
+ requestBody = decryptedBody;
+ }
ensureNonce(tenantId, appId, nonce, security);
- requestBody = decryptedBody;
+
}
// 使用可重复读取的请求包装,供后续过滤器继续消费
@@ -154,7 +163,9 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
try {
filterChain.doFilter(securedRequest, responseWrapper);
dispatchedToGateway = true;
- encryptResponse(responseWrapper, credential, security);
+ if (isNormalProcess) {
+ encryptResponse(responseWrapper, credential, security);
+ }
} finally {
responseWrapper.copyBodyToResponse();
}
@@ -163,7 +174,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
accessLogId = accessLogger.logEntrance(request);
}
log.warn("[API-PORTAL] 安全校验失败: {}", ex.getMessage());
- writeErrorResponse(response, security, credential, ex.status(), ex.getMessage());
+ writeErrorResponse(response, security, credential, ex.status(), ex.getMessage(), isNormalProcess);
if (!dispatchedToGateway) {
accessLogger.finalizeEarly(request, ex.status().value(), ex.getMessage());
}
@@ -172,7 +183,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
accessLogId = accessLogger.logEntrance(request);
}
log.error("[API-PORTAL] 处理安全校验时出现异常", ex);
- writeErrorResponse(response, security, credential, HttpStatus.INTERNAL_SERVER_ERROR, "网关安全校验失败");
+ writeErrorResponse(response, security, credential, HttpStatus.INTERNAL_SERVER_ERROR, "网关安全校验失败", isNormalProcess);
if (!dispatchedToGateway) {
accessLogger.finalizeEarly(request, HttpStatus.INTERNAL_SERVER_ERROR.value(), "网关安全校验失败");
}
@@ -479,6 +490,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
String token = tokenOptional.get();
securedRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
securedRequest.setHeader(GatewayJwtResolver.HEADER_ZT_AUTH_TOKEN, token);
+
}
private static final class SecurityValidationException extends RuntimeException {
@@ -499,7 +511,8 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
ApiGatewayProperties.Security security,
ApiClientCredentialDO credential,
HttpStatus status,
- String message) {
+ String message,
+ boolean isNormalProcess) {
if (response.isCommitted()) {
log.warn("[API-PORTAL] 响应已提交,无法写入安全校验错误: {}", message);
return;
@@ -514,7 +527,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
.response(null)
.traceId(traceId)
.build();
- if (shouldEncryptErrorResponse(security, credential)) {
+ if (shouldEncryptErrorResponse(security, credential) && isNormalProcess) {
String encryptionKey = credential.getEncryptionKey();
String encryptionType = resolveEncryptionType(credential, security);
try {