This commit is contained in:
2026-08-14 12:20:52 +08:00
parent ce0812211c
commit afd7c9d704
17 changed files with 1641 additions and 0 deletions
@@ -0,0 +1,24 @@
package com.yunhai.bschclassmanage.service;
import com.yunhai.bschclassmanage.entity.SysOperationLog;
import com.yunhai.bschclassmanage.model.vo.PageVO;
import org.springframework.data.domain.Pageable;
public interface ISysOperationLogService {
/** 切面 finally 调用,@Async("auditLogExecutor") + REQUIRES_NEW,失败仅记日志 */
void saveAsync(SysOperationLog entity);
/** 分页查询(不含 response_data 投影;实体直查,初版简单先行) */
PageVO<SysOperationLog> pageQuery(Integer schId, String uri, String method,
Integer statusCode, Integer logLevel,
Integer userId, Pageable pageable);
/** 取单条(含 response_data)用于下载 */
SysOperationLog getById(Long id);
/** 解压某条响应原文 */
String getResponseData(Long id);
/** 定时清理,返回删除条数 */
long cleanupExpired();
}
@@ -0,0 +1,96 @@
package com.yunhai.bschclassmanage.service.impl;
import com.yunhai.bschclassmanage.Repository.SysOperationLogRepository;
import com.yunhai.bschclassmanage.entity.SysOperationLog;
import com.yunhai.bschclassmanage.model.vo.PageVO;
import com.yunhai.bschclassmanage.service.ISysOperationLogService;
import com.yunhai.bschclassmanage.utils.GzipUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.criteria.Predicate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@Service
public class SysOperationLogServiceImpl implements ISysOperationLogService {
private static final int GENERAL_RETENTION_MONTHS = 12; // 一般(查询) 12 个月
private static final int IMPORTANT_RETENTION_MONTHS = 36; // 重要/次要 36 个月
@Autowired
private SysOperationLogRepository repository;
@Async("auditLogExecutor")
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveAsync(SysOperationLog entity) {
try {
repository.save(entity);
} catch (Exception e) {
log.error("审计日志异步落库失败, uri={}, method={}",
entity.getRequestUri(), entity.getRequestMethod(), e);
}
}
@Override
public PageVO<SysOperationLog> pageQuery(Integer schId, String uri, String method,
Integer statusCode, Integer logLevel,
Integer userId, Pageable pageable) {
Specification<SysOperationLog> spec = (root, query, cb) -> {
List<Predicate> ps = new ArrayList<>();
if (schId != null) ps.add(cb.equal(root.get("schId").as(Integer.class), schId));
if (StringUtils.isNotBlank(uri)) ps.add(cb.like(root.get("requestUri").as(String.class), "%" + uri + "%"));
if (StringUtils.isNotBlank(method)) ps.add(cb.equal(root.get("requestMethod").as(String.class), method.toUpperCase()));
if (statusCode != null) ps.add(cb.equal(root.get("statusCode").as(Integer.class), statusCode));
if (logLevel != null) ps.add(cb.equal(root.get("logLevel").as(Integer.class), logLevel));
if (userId != null) ps.add(cb.equal(root.get("userId").as(Integer.class), userId));
return cb.and(ps.toArray(new Predicate[0]));
};
Pageable page = pageable;
if (!page.getSort().isSorted()) {
page = PageRequest.of(page.getPageNumber(), page.getPageSize(),
Sort.by(Sort.Direction.DESC, "createTime"));
}
Page<SysOperationLog> p = repository.findAll(spec, page);
// 注意:response_data (MEDIUMBLOB) 会随实体加载,若列表性能敏感,可改用投影 VO 排除该字段
return new PageVO<>(p.getNumber() + 1, p.getSize(), p.getTotalElements(), p.getContent());
}
@Override
public SysOperationLog getById(Long id) {
return repository.findById(id).orElse(null);
}
@Override
public String getResponseData(Long id) {
SysOperationLog e = getById(id);
return (e != null && e.getResponseData() != null) ? GzipUtil.decompress(e.getResponseData()) : null;
}
@Override
@Transactional
public long cleanupExpired() {
LocalDateTime now = LocalDateTime.now();
LocalDateTime generalDeadline = now.minusMonths(GENERAL_RETENTION_MONTHS);
LocalDateTime importantDeadline = now.minusMonths(IMPORTANT_RETENTION_MONTHS);
long deleted = repository.deleteByLogLevelAndCreateTimeBefore(3, generalDeadline)
+ repository.deleteByLogLevelAndCreateTimeBefore(2, importantDeadline)
+ repository.deleteByLogLevelAndCreateTimeBefore(1, importantDeadline);
log.info("审计日志清理完成, 删除{}条(一般截止={}, 重要/次要截止={})",
deleted, generalDeadline, importantDeadline);
return deleted;
}
}