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

@@ -17,6 +17,7 @@ import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.log.annotation.LogRecord;
import javax.annotation.Resource;
import java.math.BigInteger;
@@ -39,6 +40,7 @@ public class WorkflowExecResultController extends BaseCurdController<WorkflowExe
@GetMapping("/del")
@Transactional(rollbackFor = Exception.class)
@SaCheckPermission("/api/v1/workflow/remove")
@LogRecord("删除工作流执行记录")
public Result<Void> del(BigInteger id) {
LoginAccount account = SaTokenUtil.getLoginAccount();
WorkflowExecResult record = service.getById(id);
@@ -61,4 +63,4 @@ public class WorkflowExecResultController extends BaseCurdController<WorkflowExe
}
return res;
}
}
}

View File

@@ -11,6 +11,7 @@ import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.job.entity.SysJob;
import tech.easyflow.job.service.SysJobService;
import tech.easyflow.log.annotation.LogRecord;
import tech.easyflow.common.entity.LoginAccount;
@@ -36,6 +37,7 @@ public class SysJobController extends BaseCurdController<SysJobService, SysJob>
@GetMapping("/start")
@SaCheckPermission("/api/v1/sysJob/save")
@LogRecord("启动定时任务")
public Result<Void> start(BigInteger id) {
service.startJob(id);
return Result.ok();
@@ -43,6 +45,7 @@ public class SysJobController extends BaseCurdController<SysJobService, SysJob>
@GetMapping("/stop")
@SaCheckPermission("/api/v1/sysJob/save")
@LogRecord("停止定时任务")
public Result<Void> stop(BigInteger id) {
service.stopJob(id);
return Result.ok();

View File

@@ -1,16 +1,24 @@
package tech.easyflow.admin.controller.system;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.log.annotation.LogRecord;
import tech.easyflow.system.entity.SysLog;
import tech.easyflow.system.service.SysLogService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.relation.RelationManager;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.easyflow.common.util.StringUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.log.annotation.LogRecord;
import tech.easyflow.system.entity.SysLog;
import tech.easyflow.system.service.SysLogService;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Collections;
import java.util.Date;
/**
* 操作日志表 控制层。
@@ -21,14 +29,86 @@ import java.util.Collections;
@RestController
@RequestMapping("/api/v1/sysLog")
public class SysLogController extends BaseCurdController<SysLogService, SysLog> {
private static final long MAX_PAGE_SIZE = 100L;
private static final DateTimeFormatter QUERY_TIME_FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 创建操作日志控制器。
*
* @param service 操作日志服务
*/
public SysLogController(SysLogService service) {
super(service);
}
/**
* 构造通用查询条件,并追加操作时间范围。
*
* @param request 当前 HTTP 请求
* @return 操作日志查询条件
* @throws BusinessException 时间格式不正确或开始时间晚于结束时间时抛出
*/
@Override
protected QueryWrapper buildQueryWrapper(HttpServletRequest request) {
QueryWrapper queryWrapper = super.buildQueryWrapper(request);
Date createdStart = parseQueryTime(request.getParameter("createdStart"));
Date createdEnd = parseQueryTime(request.getParameter("createdEnd"));
if (createdStart != null && createdEnd != null && createdStart.after(createdEnd)) {
throw new BusinessException(400, 400, "操作时间范围不正确");
}
if (createdStart != null) {
queryWrapper.ge(SysLog::getCreated, createdStart);
}
if (createdEnd != null) {
queryWrapper.le(SysLog::getCreated, createdEnd);
}
return queryWrapper;
}
/**
* 按操作时间稳定倒序展示最新日志。
*
* @return 默认排序表达式
*/
@Override
protected String getDefaultOrderBy() {
return "created desc, id desc";
}
/**
* 限制日志单页记录数并加载操作账号关系。
*
* @param page 分页参数
* @param queryWrapper 查询条件
* @return 操作日志分页结果
*/
@Override
@LogRecord("分页查询")
protected Page<SysLog> queryPage(Page<SysLog> page, QueryWrapper queryWrapper) {
page.setPageSize(Math.min(page.getPageSize(), MAX_PAGE_SIZE));
RelationManager.setQueryRelations(Collections.singleton("account"));
return service.getMapper().paginateWithRelations(page, queryWrapper);
}
}
/**
* 解析日志查询时间。
*
* @param value 格式为 yyyy-MM-dd HH:mm:ss 的时间文本
* @return 解析后的时间;空文本返回 {@code null}
* @throws BusinessException 时间格式不正确时抛出
*/
private Date parseQueryTime(String value) {
if (!StringUtil.hasText(value)) {
return null;
}
try {
LocalDateTime dateTime = LocalDateTime.parse(value, QUERY_TIME_FORMATTER);
return Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
} catch (DateTimeParseException exception) {
throw new BusinessException(
400, 400, "操作时间格式不正确", exception);
}
}
}

View File

@@ -0,0 +1,74 @@
package tech.easyflow.admin.controller.system;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.relation.RelationManager;
import jakarta.servlet.http.HttpServletRequest;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.Test;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.entity.SysLog;
import tech.easyflow.system.mapper.SysLogMapper;
import tech.easyflow.system.service.SysLogService;
import java.util.Collections;
import static org.testng.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* {@link SysLogController} 查询边界测试。
*/
public class SysLogControllerTest {
/**
* 清理 MyBatis-Flex 关系查询线程配置。
*/
@AfterMethod
public void tearDown() {
RelationManager.clearQueryRelations();
}
/**
* 验证日志分页最多返回一百条。
*/
@Test
public void queryPageShouldClampPageSize() {
SysLogService service = mock(SysLogService.class);
SysLogMapper mapper = mock(SysLogMapper.class);
when(service.getMapper()).thenReturn(mapper);
when(mapper.paginateWithRelations(any(Page.class), any(QueryWrapper.class)))
.thenAnswer(invocation -> invocation.getArgument(0));
SysLogController controller = new SysLogController(service);
Page<SysLog> page = new Page<>(1, 500);
Page<SysLog> result = controller.queryPage(page, QueryWrapper.create());
assertEquals(100L, result.getPageSize());
}
/**
* 验证日志默认按操作时间稳定倒序排列。
*/
@Test
public void defaultOrderShouldUseCreatedAndId() {
SysLogController controller = new SysLogController(mock(SysLogService.class));
assertEquals(controller.getDefaultOrderBy(), "created desc, id desc");
}
/**
* 验证错误的时间格式会返回明确的业务参数错误。
*/
@Test(expectedExceptions = BusinessException.class)
public void buildQueryWrapperShouldRejectInvalidTime() {
SysLogController controller = new SysLogController(mock(SysLogService.class));
HttpServletRequest request = mock(HttpServletRequest.class);
when(request.getParameterMap()).thenReturn(Collections.emptyMap());
when(request.getParameter("createdStart")).thenReturn("2026/07/30");
controller.buildQueryWrapper(request);
}
}

View File

@@ -17,6 +17,7 @@ import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.common.web.jsonbody.JsonBody;
import tech.easyflow.log.annotation.LogRecord;
import javax.annotation.Resource;
import java.math.BigInteger;
@@ -65,6 +66,7 @@ public class UcBotRecentlyUsedController extends BaseCurdController<BotRecentlyU
}
@GetMapping("/removeByBotId")
@LogRecord("移除最近使用智能体")
public Result<Void> removeByBotId(BigInteger botId) {
QueryWrapper w = QueryWrapper.create();
w.eq(BotRecentlyUsed::getBotId,botId);

View File

@@ -19,6 +19,7 @@ import tech.easyflow.common.domain.Result;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.satoken.util.SaTokenUtil;
import tech.easyflow.common.web.controller.BaseCurdController;
import tech.easyflow.log.annotation.LogRecord;
import javax.annotation.Resource;
import java.math.BigInteger;
@@ -44,6 +45,7 @@ public class UcWorkflowExecResultController extends BaseCurdController<WorkflowE
@GetMapping("/del")
@Transactional(rollbackFor = Exception.class)
@SaCheckPermission("/api/v1/workflow/remove")
@LogRecord("删除工作流执行记录")
public Result<Void> del(BigInteger id) {
LoginAccount account = SaTokenUtil.getLoginAccount();
WorkflowExecResult record = service.getById(id);
@@ -89,4 +91,4 @@ public class UcWorkflowExecResultController extends BaseCurdController<WorkflowE
}
return res;
}
}
}