Merge branch 'dev' into test

This commit is contained in:
chenbowen
2025-11-18 10:59:55 +08:00
73 changed files with 4998 additions and 93 deletions

View File

@@ -7,17 +7,23 @@ import com.zt.plat.module.databus.controller.admin.gateway.vo.accesslog.ApiAcces
import com.zt.plat.module.databus.controller.admin.gateway.vo.accesslog.ApiAccessLogRespVO;
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiAccessLogDO;
import com.zt.plat.module.databus.service.gateway.ApiAccessLogService;
import com.zt.plat.module.databus.service.gateway.ApiDefinitionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
@@ -33,13 +39,18 @@ public class ApiAccessLogController {
@Resource
private ApiAccessLogService apiAccessLogService;
@Resource
private ApiDefinitionService apiDefinitionService;
@GetMapping("/get")
@Operation(summary = "获取访问日志详情")
@Parameter(name = "id", description = "日志编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('databus:gateway:access-log:query')")
public CommonResult<ApiAccessLogRespVO> get(@RequestParam("id") Long id) {
ApiAccessLogDO logDO = apiAccessLogService.get(id);
return success(ApiAccessLogConvert.INSTANCE.convert(logDO));
ApiAccessLogRespVO respVO = ApiAccessLogConvert.INSTANCE.convert(logDO);
enrichDefinitionInfo(respVO);
return success(respVO);
}
@GetMapping("/page")
@@ -47,6 +58,51 @@ public class ApiAccessLogController {
@PreAuthorize("@ss.hasPermission('databus:gateway:access-log:query')")
public CommonResult<PageResult<ApiAccessLogRespVO>> page(@Valid ApiAccessLogPageReqVO pageReqVO) {
PageResult<ApiAccessLogDO> pageResult = apiAccessLogService.getPage(pageReqVO);
return success(ApiAccessLogConvert.INSTANCE.convertPage(pageResult));
PageResult<ApiAccessLogRespVO> result = ApiAccessLogConvert.INSTANCE.convertPage(pageResult);
enrichDefinitionInfo(result.getList());
return success(result);
}
private void enrichDefinitionInfo(List<ApiAccessLogRespVO> list) {
// 对分页结果批量补充 API 描述,使用本地缓存减少重复查询
if (CollectionUtils.isEmpty(list)) {
return;
}
Map<String, String> cache = new HashMap<>(list.size());
list.forEach(item -> {
if (item == null) {
return;
}
String cacheKey = buildCacheKey(item.getApiCode(), item.getApiVersion());
if (!cache.containsKey(cacheKey)) {
cache.put(cacheKey, resolveApiDescription(item.getApiCode(), item.getApiVersion()));
}
item.setApiDescription(cache.get(cacheKey));
});
}
private void enrichDefinitionInfo(ApiAccessLogRespVO item) {
// 单条数据同样需要补全描述信息
if (item == null) {
return;
}
item.setApiDescription(resolveApiDescription(item.getApiCode(), item.getApiVersion()));
}
private String resolveApiDescription(String apiCode, String apiVersion) {
if (!StringUtils.hasText(apiCode)) {
return null;
}
String normalizedVersion = StringUtils.hasText(apiVersion) ? apiVersion.trim() : apiVersion;
// 通过网关定义服务补全 API 描述,提升页面可读性
return apiDefinitionService.findByCodeAndVersionIncludingInactive(apiCode, normalizedVersion)
.map(aggregate -> aggregate.getDefinition() != null ? aggregate.getDefinition().getDescription() : null)
.filter(StringUtils::hasText)
.orElse(null);
}
private String buildCacheKey(String apiCode, String apiVersion) {
// 组合唯一键,避免重复查询相同的 API 描述
return (apiCode == null ? "" : apiCode) + "#" + (apiVersion == null ? "" : apiVersion);
}
}

View File

@@ -4,7 +4,9 @@ import com.zt.plat.framework.common.pojo.PageResult;
import com.zt.plat.module.databus.controller.admin.gateway.vo.accesslog.ApiAccessLogRespVO;
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiAccessLogDO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import org.springframework.http.HttpStatus;
import java.util.List;
@@ -13,6 +15,8 @@ public interface ApiAccessLogConvert {
ApiAccessLogConvert INSTANCE = Mappers.getMapper(ApiAccessLogConvert.class);
@Mapping(target = "statusDesc", expression = "java(statusDesc(bean.getStatus()))")
@Mapping(target = "responseStatusText", expression = "java(resolveHttpStatusText(bean.getResponseStatus()))")
ApiAccessLogRespVO convert(ApiAccessLogDO bean);
List<ApiAccessLogRespVO> convertList(List<ApiAccessLogDO> list);
@@ -26,4 +30,26 @@ public interface ApiAccessLogConvert {
result.setTotal(page.getTotal());
return result;
}
default String statusDesc(Integer status) {
// 将数字状态码转换为中文描述,方便前端直接展示
if (status == null) {
return "未知";
}
return switch (status) {
case 0 -> "成功";
case 1 -> "客户端错误";
case 2 -> "服务端错误";
default -> "未知";
};
}
default String resolveHttpStatusText(Integer status) {
// 统一使用 Spring 的 HttpStatus 解析出标准文案
if (status == null) {
return null;
}
HttpStatus resolved = HttpStatus.resolve(status);
return resolved != null ? resolved.getReasonPhrase() : null;
}
}

View File

@@ -21,6 +21,9 @@ public class ApiAccessLogRespVO {
@Schema(description = "API 编码", example = "user.query")
private String apiCode;
@Schema(description = "API 描述", example = "用户查询服务")
private String apiDescription;
@Schema(description = "API 版本", example = "v1")
private String apiVersion;
@@ -42,6 +45,9 @@ public class ApiAccessLogRespVO {
@Schema(description = "响应 HTTP 状态", example = "200")
private Integer responseStatus;
@Schema(description = "响应 HTTP 状态说明", example = "OK")
private String responseStatusText;
@Schema(description = "响应提示", example = "OK")
private String responseMessage;
@@ -51,6 +57,9 @@ public class ApiAccessLogRespVO {
@Schema(description = "访问状态", example = "0")
private Integer status;
@Schema(description = "访问状态展示文案", example = "成功")
private String statusDesc;
@Schema(description = "错误码", example = "DAT-001")
private String errorCode;

View File

@@ -28,7 +28,7 @@ public class ApiAccessLogDO extends TenantBaseDO {
private Long id;
/**
* 请求追踪标识,对应 {@link com.zt.plat.module.databus.framework.integration.gateway.model.ApiInvocationContext#getRequestId()}
* 请求追踪标识,对应 {@link com.zt.plat.framework.common.util.monitor.TracerUtils#getTraceId()}
*/
private String traceId;

View File

@@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zt.plat.framework.common.exception.ServiceException;
import com.zt.plat.framework.common.exception.util.ServiceExceptionUtil;
import com.zt.plat.framework.common.util.monitor.TracerUtils;
import com.zt.plat.module.databus.controller.admin.gateway.vo.ApiGatewayInvokeReqVO;
import com.zt.plat.module.databus.framework.integration.config.ApiGatewayProperties;
import com.zt.plat.module.databus.framework.integration.gateway.domain.ApiDefinitionAggregate;
@@ -236,12 +237,12 @@ public class ApiGatewayExecutionService {
String message = StringUtils.hasText(context.getResponseMessage())
? context.getResponseMessage()
: HttpStatus.valueOf(status).getReasonPhrase();
return ApiGatewayResponse.builder()
return ApiGatewayResponse.builder()
.code(status)
.message(message)
.response(context.getResponseBody())
.traceId(context.getRequestId())
.build();
.message(message)
.response(context.getResponseBody())
.traceId(TracerUtils.getTraceId())
.build();
}
private String normalizeBasePath(String basePath) {

View File

@@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zt.plat.framework.common.util.json.JsonUtils;
import com.zt.plat.framework.common.util.monitor.TracerUtils;
import com.zt.plat.framework.common.util.security.CryptoSignatureUtils;
import com.zt.plat.framework.common.util.servlet.ServletUtils;
import com.zt.plat.framework.security.core.LoginUser;
@@ -464,11 +465,12 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
response.resetBuffer();
response.setStatus(status.value());
String resolvedMessage = StringUtils.hasText(message) ? message : status.getReasonPhrase();
ApiGatewayResponse envelope = ApiGatewayResponse.builder()
String traceId = TracerUtils.getTraceId();
ApiGatewayResponse envelope = ApiGatewayResponse.builder()
.code(status.value())
.message(resolvedMessage)
.response(null)
.traceId(null)
.traceId(traceId)
.build();
if (shouldEncryptErrorResponse(security, credential)) {
String encryptionKey = credential.getEncryptionKey();