feat(pigx-common-audit): 重构审计模块,支持通过主键自动获取对象进行比对

This commit is contained in:
PIGCLOUD committed 2026-05-18 14:19:08 +08:00
1 parent 836fc04a26
commit 1d9bfd866f
5 files changed
+141 -10

No files matched your search

@@ -6,10 +6,12 @@ import com.pig4cloud.pigx.common.audit.handle.DefaultAuditLogHandle;
import com.pig4cloud.pigx.common.audit.handle.IAuditLogHandle;
import com.pig4cloud.pigx.common.audit.handle.ICompareHandle;
import com.pig4cloud.pigx.common.audit.handle.JavesCompareHandle;
import com.pig4cloud.pigx.common.audit.support.AuditValueResolver;
import com.pig4cloud.pigx.common.audit.support.SpelParser;
import com.pig4cloud.pigx.common.core.util.KeyStrResolver;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.EnableAsync;
@@ -34,8 +36,22 @@ public class AuditAutoConfiguration {
*/
@Bean
@ConditionalOnMissingBean
public ICompareHandle compareHandle(Optional<IAuditLogHandle> auditNameHandleOptional) {
return new JavesCompareHandle(auditNameHandleOptional);
public ICompareHandle compareHandle(Optional<IAuditLogHandle> auditNameHandleOptional,
AuditValueResolver auditValueResolver) {
return new JavesCompareHandle(auditNameHandleOptional, auditValueResolver);
}
/**
* 审计值解析器。
*
* @param applicationContext Spring 上下文
* @return AuditValueResolver
* @since 6.0
*/
@Bean
@ConditionalOnMissingBean
public AuditValueResolver auditValueResolver(ApplicationContext applicationContext) {
return new AuditValueResolver(applicationContext);
}
/**
@@ -44,4 +44,22 @@ public @interface Audit {
*/
String newVal() default "";
/**
* 主键表达式(SpEL)。配置后会通过 MyBatis-Plus Mapper selectById 查询切面执行前后的实体快照,
* 解析结果必须实现 {@link java.io.Serializable}。
*
* @return 主键 SpEL 表达式
* @since 6.0
*/
String id() default "";
/**
* 显式指定 MyBatis-Plus Mapper 类型,必须是被 Spring 管理的 BaseMapper Bean。
* 默认 {@code Void.class} 表示从当前 Service 的 {@code getBaseMapper()} 自动获取。
*
* @return Mapper 类型
* @since 6.0
*/
Class<?> mapper() default Void.class;
}
@@ -2,12 +2,11 @@ package com.pig4cloud.pigx.common.audit.aop;
import com.pig4cloud.pigx.common.audit.annotation.Audit;
import com.pig4cloud.pigx.common.audit.handle.ICompareHandle;
import com.pig4cloud.pigx.common.audit.support.SpelParser;
import com.pig4cloud.pigx.common.audit.support.AuditValueResolver;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.util.StringUtils;
/**
* @author lengleng
@@ -22,11 +21,12 @@ public class AuditAspect {
private final ICompareHandle compareHandle;
private final AuditValueResolver auditValueResolver;
@Around("@annotation(audit)")
public Object auditLog(ProceedingJoinPoint joinPoint, Audit audit) throws Throwable {
// 获取变更之前的结果
Object oldVal = SpelParser.parser(joinPoint,
StringUtils.hasText(audit.oldVal()) ? audit.oldVal() : audit.spel());
Object oldVal = auditValueResolver.resolveOldVal(joinPoint, audit);
Object result = joinPoint.proceed();
compareHandle.compare(oldVal, joinPoint, audit);
@@ -1,12 +1,11 @@
package com.pig4cloud.pigx.common.audit.handle;
import com.pig4cloud.pigx.common.audit.annotation.Audit;
import com.pig4cloud.pigx.common.audit.support.AuditValueResolver;
import com.pig4cloud.pigx.common.audit.support.DataAuditor;
import com.pig4cloud.pigx.common.audit.support.SpelParser;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.javers.core.Changes;
import org.springframework.util.StringUtils;
import java.util.Optional;
@@ -21,14 +20,15 @@ public class JavesCompareHandle implements ICompareHandle {
private final Optional<IAuditLogHandle> auditLogHandleOptional;
private final AuditValueResolver auditValueResolver;
/**
* 比较两个对象是否变更,及其变更后如何审计
* @param oldVal 原有值
*/
@Override
public Changes compare(Object oldVal, ProceedingJoinPoint joinPoint, Audit audit) {
Object newVal = SpelParser.parser(joinPoint,
StringUtils.hasText(audit.newVal()) ? audit.newVal() : audit.spel());
Object newVal = auditValueResolver.resolveNewVal(joinPoint, audit);
Changes compare = DataAuditor.compare(oldVal, newVal);
auditLogHandleOptional.ifPresent(handle -> handle.handle(audit, compare));
return compare;
@@ -0,0 +1,97 @@
package com.pig4cloud.pigx.common.audit.support;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.pig4cloud.pigx.common.audit.annotation.Audit;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.Objects;
/**
* 审计快照解析器。
*
* @author lengleng
* @since 6.0
*/
@RequiredArgsConstructor
public class AuditValueResolver {
private final ApplicationContext applicationContext;
/**
* 解析修改前快照。
*
* @param joinPoint 连接点
* @param audit 审计注解
* @return 修改前快照
*/
public Object resolveOldVal(ProceedingJoinPoint joinPoint, Audit audit) {
if (StringUtils.hasText(audit.id())) {
return selectById(joinPoint, audit);
}
return SpelParser.parser(joinPoint, StringUtils.hasText(audit.oldVal()) ? audit.oldVal() : audit.spel());
}
/**
* 解析修改后快照。
*
* @param joinPoint 连接点
* @param audit 审计注解
* @return 修改后快照
*/
public Object resolveNewVal(ProceedingJoinPoint joinPoint, Audit audit) {
if (StringUtils.hasText(audit.id())) {
return selectById(joinPoint, audit);
}
return SpelParser.parser(joinPoint, StringUtils.hasText(audit.newVal()) ? audit.newVal() : audit.spel());
}
private Object selectById(ProceedingJoinPoint joinPoint, Audit audit) {
Object id = SpelParser.parser(joinPoint, audit.id());
if (Objects.isNull(id)) {
return null;
}
if (!(id instanceof Serializable serializableId)) {
throw new IllegalArgumentException("@Audit id 表达式解析结果必须实现 Serializable");
}
return resolveMapper(joinPoint, audit).selectById(serializableId);
}
private BaseMapper<?> resolveMapper(ProceedingJoinPoint joinPoint, Audit audit) {
Object mapper;
if (audit.mapper() == Void.class) {
mapper = getBaseMapper(joinPoint);
} else {
try {
mapper = applicationContext.getBean(audit.mapper());
} catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException(
"未找到 @Audit mapper Bean: " + audit.mapper().getName() + ",请确认其已注册到 MyBatis-Plus", ex);
}
}
if (mapper instanceof BaseMapper<?> baseMapper) {
return baseMapper;
}
throw new IllegalStateException("@Audit mapper 必须是 MyBatis-Plus BaseMapper 类型");
}
private Object getBaseMapper(ProceedingJoinPoint joinPoint) {
Object target = Objects.nonNull(joinPoint.getTarget()) ? joinPoint.getTarget() : joinPoint.getThis();
if (Objects.isNull(target)) {
throw new IllegalStateException("@Audit 未指定 mapper,且无法获取当前 Service 实例");
}
Method getBaseMapper = ReflectionUtils.findMethod(target.getClass(), "getBaseMapper");
if (Objects.isNull(getBaseMapper)) {
throw new IllegalStateException("@Audit 未指定 mapper,当前 Service 未提供 getBaseMapper()");
}
ReflectionUtils.makeAccessible(getBaseMapper);
return ReflectionUtils.invokeMethod(getBaseMapper, target);
}
}