feat(sysapi): 添加接口日志归档功能

- 创建 SysApiLogHistory 实体类用于存储历史日志数据
- 实现 ISysApiLogHistoryService 和相关 Mapper 接口
- 添加 SysApiLogHistoryServiceImpl 业务逻辑实现
- 在 SysApiLogService 中新增 archiveLogs 方法实现归档逻辑
- 更新 SysApiLogController 支持日志归档接口调用
- 实现按系统参数配置的保留天数自动迁移旧日志数据
- 添加事务支持确保归档过程的数据一致性
- 集成 SpringContextHolder 用于获取系统参数配置
This commit is contained in:
yangyufu
2026-05-26 16:47:44 +08:00
parent 288b35e94f
commit d63437e01b
7 changed files with 375 additions and 20 deletions

View File

@@ -1,5 +1,6 @@
package org.nl.system.controller.sysapi;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -7,11 +8,16 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.nl.common.domain.query.PageQuery;
import org.nl.modules.logging.annotation.Log;
import org.nl.modules.wql.util.SpringContextHolder;
import org.nl.system.service.param.impl.SysParamServiceImpl;
import org.nl.system.service.sysapi.entity.SysApiLog;
import org.nl.system.service.sysapi.entity.SysApiLogHistory;
import org.nl.system.service.sysapi.entity.dto.ApiLogQuery;
import org.nl.system.service.sysapi.service.ISysApiLogHistoryService;
import org.nl.system.service.sysapi.service.ISysApiLogService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import java.util.*;
@@ -31,6 +37,8 @@ public class SysApiLogController {
private final ISysApiLogService apiLogService;
private final ISysApiLogHistoryService apiLogHistoryService;
/**
* 分页查询接口日志列表
*/
@@ -135,25 +143,8 @@ public class SysApiLogController {
*/
@PostMapping("/archive")
@Log("接口日志归档")
public ResponseEntity<Void> archiveLogs() {
try {
LambdaQueryWrapper<SysApiLog> wrapper = new LambdaQueryWrapper<>();
wrapper.lt(SysApiLog::getCreateTime,
java.time.LocalDateTime.now().minusDays(30).toString().replace('T', ' '));
List<SysApiLog> oldLogs = apiLogService.list(wrapper);
if (!oldLogs.isEmpty()) {
log.info("开始归档接口日志,共 {} 条数据", oldLogs.size());
// TODO: 这里可以实现归档逻辑,比如:
// 1. 备份到历史表 sys_api_log_history
apiLogService.remove(wrapper);
log.info("接口日志归档完成,已删除 {} 条旧数据", oldLogs.size());
}
return new ResponseEntity<>(HttpStatus.OK);
} catch (Exception e) {
log.error("接口日志归档失败", e);
throw new RuntimeException("日志归档失败: " + e.getMessage());
}
@Transactional(rollbackFor = Exception.class)
public ResponseEntity archiveLogs() {
return apiLogService.archiveLogs();
}
}

View File

@@ -0,0 +1,94 @@
package org.nl.system.service.sysapi.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.beans.BeanUtils;
import java.io.Serializable;
/**
* 接口日志对象 sys_api_log_history
*
* @author ruoyi
* @date 2026-05-26
*/
@Data
@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
@Accessors(chain = true)
@TableName("sys_api_log_history")
public class SysApiLogHistory implements Serializable
{
private static final long serialVersionUID = 1L;
@TableId("log_id")
private String logId;
@TableField("direction")
private Integer direction;
@TableField("system_flag")
private String systemFlag;
@TableField("biz_code")
private String bizCode;
@TableField("biz_desc")
private String bizDesc;
@TableField("api_url")
private String apiUrl;
@TableField("api_desc")
private String apiDesc;
@TableField("trace_id")
private String traceId;
@TableField("request_method")
private String requestMethod;
@TableField("request_ip")
private String requestIp;
@TableField("request_headers")
private String requestHeaders;
@TableField("request_params")
private String requestParams;
@TableField("response_body")
private String responseBody;
@TableField("response_status")
private Integer responseStatus;
@TableField("cost_time")
private Long costTime;
@TableField("status")
private String status;
@TableField("error_msg")
private String errorMsg;
@TableField("operator")
private String operator;
@TableField("create_time")
private String createTime;
//copy method
public static SysApiLogHistory adapter(SysApiLog sysApiLog){
SysApiLogHistory sysApiLogHistory = new SysApiLogHistory();
BeanUtils.copyProperties(sysApiLog,sysApiLogHistory);
return sysApiLogHistory;
}
}

View File

@@ -0,0 +1,61 @@
package org.nl.system.service.sysapi.mapper;
import java.util.List;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.nl.system.service.sysapi.entity.SysApiLogHistory;
/**
* 接口日志Mapper接口
*
* @author ruoyi
* @date 2026-05-26
*/
public interface SysApiLogHistoryMapper extends BaseMapper<SysApiLogHistory> {
/**
* 查询接口日志
*
* @param logId 接口日志主键
* @return 接口日志
*/
public SysApiLogHistory selectSysApiLogHistoryByLogId(String logId);
/**
* 查询接口日志列表
*
* @param sysApiLogHistory 接口日志
* @return 接口日志集合
*/
public List<SysApiLogHistory> selectSysApiLogHistoryList(SysApiLogHistory sysApiLogHistory);
/**
* 新增接口日志
*
* @param sysApiLogHistory 接口日志
* @return 结果
*/
public int insertSysApiLogHistory(SysApiLogHistory sysApiLogHistory);
/**
* 修改接口日志
*
* @param sysApiLogHistory 接口日志
* @return 结果
*/
public int updateSysApiLogHistory(SysApiLogHistory sysApiLogHistory);
/**
* 删除接口日志
*
* @param logId 接口日志主键
* @return 结果
*/
public int deleteSysApiLogHistoryByLogId(String logId);
/**
* 批量删除接口日志
*
* @param logIds 需要删除的数据主键集合
* @return 结果
*/
public int deleteSysApiLogHistoryByLogIds(String[] logIds);
}

View File

@@ -0,0 +1,61 @@
package org.nl.system.service.sysapi.service;
import java.util.List;
import com.baomidou.mybatisplus.extension.service.IService;
import org.nl.system.service.sysapi.entity.SysApiLogHistory;
/**
* 接口日志Service接口
*
* @author ruoyi
* @date 2026-05-26
*/
public interface ISysApiLogHistoryService extends IService<SysApiLogHistory> {
/**
* 查询接口日志
*
* @param logId 接口日志主键
* @return 接口日志
*/
public SysApiLogHistory selectSysApiLogHistoryByLogId(String logId);
/**
* 查询接口日志列表
*
* @param sysApiLogHistory 接口日志
* @return 接口日志集合
*/
public List<SysApiLogHistory> selectSysApiLogHistoryList(SysApiLogHistory sysApiLogHistory);
/**
* 新增接口日志
*
* @param sysApiLogHistory 接口日志
* @return 结果
*/
public int insertSysApiLogHistory(SysApiLogHistory sysApiLogHistory);
/**
* 修改接口日志
*
* @param sysApiLogHistory 接口日志
* @return 结果
*/
public int updateSysApiLogHistory(SysApiLogHistory sysApiLogHistory);
/**
* 批量删除接口日志
*
* @param logIds 需要删除的接口日志主键集合
* @return 结果
*/
public int deleteSysApiLogHistoryByLogIds(String[] logIds);
/**
* 删除接口日志信息
*
* @param logId 接口日志主键
* @return 结果
*/
public int deleteSysApiLogHistoryByLogId(String logId);
}

View File

@@ -1,6 +1,8 @@
package org.nl.system.service.sysapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.apache.poi.ss.formula.functions.T;
import org.nl.system.service.sysapi.entity.SysApiLog;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
/**
@@ -14,4 +16,6 @@ public interface ISysApiLogService extends IService<SysApiLog> {
@Async("apiLogExecutor")
void saveAsync(SysApiLog apiLog);
ResponseEntity<T> archiveLogs();
}

View File

@@ -0,0 +1,88 @@
package org.nl.system.service.sysapi.service;
import com.alibaba.fastjson2.util.DateUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.List;
import org.nl.system.service.sysapi.entity.SysApiLogHistory;
import org.nl.system.service.sysapi.mapper.SysApiLogHistoryMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 接口日志Service业务层处理
*
* @author ruoyi
* @date 2026-05-26
*/
@Service
public class SysApiLogHistoryServiceImpl extends ServiceImpl<SysApiLogHistoryMapper, SysApiLogHistory> implements ISysApiLogHistoryService {
@Autowired
private SysApiLogHistoryMapper sysApiLogHistoryMapper;
/**
* 查询接口日志
*
* @param logId 接口日志主键
* @return 接口日志
*/
@Override
public SysApiLogHistory selectSysApiLogHistoryByLogId(String logId) {
return sysApiLogHistoryMapper.selectSysApiLogHistoryByLogId(logId);
}
/**
* 查询接口日志列表
*
* @param sysApiLogHistory 接口日志
* @return 接口日志
*/
@Override
public List<SysApiLogHistory> selectSysApiLogHistoryList(SysApiLogHistory sysApiLogHistory) {
return sysApiLogHistoryMapper.selectSysApiLogHistoryList(sysApiLogHistory);
}
/**
* 新增接口日志
*
* @param sysApiLogHistory 接口日志
* @return 结果
*/
@Override
public int insertSysApiLogHistory(SysApiLogHistory sysApiLogHistory) {
return sysApiLogHistoryMapper.insertSysApiLogHistory(sysApiLogHistory);
}
/**
* 修改接口日志
*
* @param sysApiLogHistory 接口日志
* @return 结果
*/
@Override
public int updateSysApiLogHistory(SysApiLogHistory sysApiLogHistory) {
return sysApiLogHistoryMapper.updateSysApiLogHistory(sysApiLogHistory);
}
/**
* 批量删除接口日志
*
* @param logIds 需要删除的接口日志主键
* @return 结果
*/
@Override
public int deleteSysApiLogHistoryByLogIds(String[] logIds) {
return sysApiLogHistoryMapper.deleteSysApiLogHistoryByLogIds(logIds);
}
/**
* 删除接口日志信息
*
* @param logId 接口日志主键
* @return 结果
*/
@Override
public int deleteSysApiLogHistoryByLogId(String logId) {
return sysApiLogHistoryMapper.deleteSysApiLogHistoryByLogId(logId);
}
}

View File

@@ -1,12 +1,26 @@
package org.nl.system.service.sysapi.service;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.nl.common.utils.IdUtil;
import org.nl.modules.wql.util.SpringContextHolder;
import org.nl.system.service.param.impl.SysParamServiceImpl;
import org.nl.system.service.sysapi.entity.SysApiLog;
import org.nl.system.service.sysapi.entity.SysApiLogHistory;
import org.nl.system.service.sysapi.mapper.SysApiLogHistoryMapper;
import org.nl.system.service.sysapi.mapper.SysApiLogMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author ManMan.Yang
@@ -20,6 +34,9 @@ import org.springframework.stereotype.Service;
@Slf4j
public class SysApiLogServiceImpl extends ServiceImpl<SysApiLogMapper, SysApiLog> implements ISysApiLogService {
@Autowired
private ISysApiLogHistoryService sysApiLogHistoryService;
@Override
@Async("apiLogExecutor")
public void saveAsync(SysApiLog apiLog) {
@@ -35,4 +52,43 @@ public class SysApiLogServiceImpl extends ServiceImpl<SysApiLogMapper, SysApiLog
log.error("保存接口日志失败, logId: {}", apiLog.getLogId(), e);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public ResponseEntity<T> archiveLogs() {
try {
// 获取系统配置的日志保留天数
String days = SpringContextHolder.getBean(SysParamServiceImpl.class).findByCode("sys_api_keep_days").getValue();
if(StrUtil.isEmpty( days)){
days = "60";
}
int keepDays = Integer.parseInt(days);
LambdaQueryWrapper<SysApiLog> wrapper = new LambdaQueryWrapper<>();
wrapper.lt(SysApiLog::getCreateTime,
java.time.LocalDateTime.now().minusDays(keepDays).toString().replace('T', ' '));
List<SysApiLog> oldLogs = this.list(wrapper);
if (!oldLogs.isEmpty()) {
log.info("开始归档接口日志,共 {} 条数据", oldLogs.size());
/*
* 88549333 接口日志归档,防止数据量过大对插入和查询造成性能损耗
* 1. 批量保存到历史表 sys_api_log_history
* 2. 删除旧数据
*/
// 1. 备份到历史表 sys_api_log_history
List<SysApiLogHistory> historyList = oldLogs.stream().map(SysApiLogHistory::adapter).collect(Collectors.toList());
boolean saveSuccess = sysApiLogHistoryService.saveBatch(historyList);
if (saveSuccess) {
this.remove(wrapper);
log.info("接口日志归档完成,已迁移并删除 {} 条旧数据", oldLogs.size());
} else {
log.error("接口日志归档失败,保存历史数据失败");
throw new RuntimeException("保存历史数据失败");
}
}
return new ResponseEntity<>(HttpStatus.OK);
} catch (Exception e) {
log.error("接口日志归档失败", e);
throw new RuntimeException("日志归档失败: " + e.getMessage());
}
}
}