perf: 优化日志保留与分页查询

- 降低普通只读请求的日志写入并保留敏感 GET 审计

- 增加数据库分批清理、时间索引和文件滚动容量上限

- 增加近 30 天筛选、稳定倒序和分页大小限制
This commit is contained in:
2026-07-30 14:21:36 +08:00
parent c78074a969
commit 03f45212ef
21 changed files with 732 additions and 19 deletions

View File

@@ -26,7 +26,9 @@ import java.util.Date;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
/**
* 记录管理端 Controller 操作结果及最小必要请求摘要。
@@ -43,20 +45,38 @@ public class LogAspect {
private static final String SKILL_API_PREFIX = "/api/v1/skill";
private static final List<String> SKILL_IDENTIFIER_KEYS =
List.of("id", "resourceId", "skillId", "sourceId");
private static final Set<String> READ_ONLY_HTTP_METHODS =
Set.of("GET", "HEAD", "OPTIONS");
private final WriteLogMapper logService;
private final LogRecordProperties config;
/**
* 创建操作日志切面。
*
* @param logService 操作日志写入 Mapper
* @param config 操作日志配置
*/
public LogAspect(WriteLogMapper logService, LogRecordProperties config) {
this.logService = logService;
this.config = config;
}
/**
* 匹配管理端控制器与通用增删改查控制器方法。
*/
@Pointcut("within(@org.springframework.web.bind.annotation.RestController *) " +
"|| execution(* tech.easyflow.common.web.controller.BaseCurdController.*(..))")
public void pointcut() {
}
/**
* 执行业务方法并按配置持久化操作日志。
*
* @param proceedingJoinPoint 当前连接点
* @return 业务方法返回值
* @throws Throwable 业务方法或日志写入异常
*/
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
@@ -72,6 +92,10 @@ public class LogAspect {
MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
Class<?> controllerClass = signature.getDeclaringType();
Method method = signature.getMethod();
LogRecord logRecord = method.getAnnotation(LogRecord.class);
if (!shouldRecordRequest(request, logRecord)) {
return proceedingJoinPoint.proceed();
}
String params = getRequestParamsString(request);
Throwable actionFailure = null;
@@ -82,7 +106,6 @@ public class LogAspect {
throw throwable;
} finally {
WriteLog sysLog = new WriteLog();
LogRecord logRecord = method.getAnnotation(LogRecord.class);
if (StpUtil.isLogin()) {
BigInteger accountId = SaTokenUtil.getLoginAccount().getId();
@@ -103,6 +126,22 @@ public class LogAspect {
}
}
/**
* 判断当前请求是否需要写入操作日志。
*
* @param request 当前 HTTP 请求
* @param logRecord 方法上的显式日志标记
* @return 需要记录返回 {@code true}
*/
private boolean shouldRecordRequest(HttpServletRequest request, LogRecord logRecord) {
if (config.isRecordReadActions() || logRecord != null) {
return true;
}
String httpMethod = request.getMethod();
return httpMethod == null
|| !READ_ONLY_HTTP_METHODS.contains(httpMethod.toUpperCase(Locale.ROOT));
}
/**
* 持久化操作日志;业务动作已失败时,日志异常不得覆盖原始异常。
*
@@ -123,6 +162,13 @@ public class LogAspect {
}
}
/**
* 解析日志动作名称。
*
* @param logRecord 显式日志标记
* @param method 当前控制器方法
* @return 日志动作名称
*/
private String buildActionName(LogRecord logRecord, Method method) {
if (logRecord != null && StringUtil.hasText(logRecord.value())) {
return logRecord.value();
@@ -199,6 +245,12 @@ public class LogAspect {
return null;
}
/**
* 提取并限制请求查询参数,避免超长内容进入数据库。
*
* @param request 当前 HTTP 请求
* @return 有界查询参数文本
*/
private String getRequestParamsString(HttpServletRequest request) {
StringBuilder sb = new StringBuilder();
Enumeration<String> e = request.getParameterNames();