posts - 46,  comments - 9,  views - 13万
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

一、定义切面对象

复制代码
import java.lang.annotation.*;

/**
 * 字段变更日志注解
 *
 * @author zhuzhen
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogFieldChange {

    /**
     * 业务类型,枚举针对不同的业务定义不同的对象,以及需要判断发生数据变更的字段名
     */
    FieldChangeBizTypeEnum bizType();

    /**
     * 业务主键字段名,eg:#product.id
     */
    String bizIdField();

    /**
     * 业务编码字段名
     */
    String bizNoField() default "";

    /**
     * 租户编码
     */
    String tenantCode();

    /**
     * 操作人
     */
    String operator();

}
复制代码

 

复制代码
@Getter
public enum FieldChangeBizTypeEnum {

    // 结算标志业务下需要记录发生变更的字段
    SM_REPAIR_SETTLEMENT_STANDARD("settlement_standard", "结算标准",
            Arrays.asList("productLine", "productType", "projectCode", "coolingCapacityMax",
                    "coolingCapacityMin", "baseRepairCost", "extraRepairCost", "repairStandardItemCode", "repairType", "completionTime")),

    ;

    private final String code;

    private final String desc;
    
  // 需要判断是否发生变更的字段
private final List<String> fieldList; FieldChangeBizTypeEnum(String code, String desc, List<String> fieldList) { this.code = code; this.desc = desc; this.fieldList = fieldList; } }
复制代码

 

二、AOP切面拦截处理EL表达式解析

类:ExpressionEvaluator定义。可无脑使用

复制代码
import org.springframework.aop.support.AopUtils;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.context.expression.CachedExpressionEvaluator;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ExpressionEvaluator<T> extends CachedExpressionEvaluator {

    private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    private final Map<ExpressionKey, Expression> conditionCache = new ConcurrentHashMap<>(64);

    private final Map<AnnotatedElementKey, Method> targetMethodCache = new ConcurrentHashMap<>(64);

    public EvaluationContext createEvaluationContext(Object o, Class<?> targetClass, Method method, Object[] args) {
        Method targetMethod = getTargetMethod(targetClass, method);

        ExpressionRootObject root = new ExpressionRootObject(o, args);
        return new MethodBasedEvaluationContext(root, targetMethod, args, this.parameterNameDiscoverer);
    }

    public T condition(String conditionExpression, AnnotatedElementKey elementKey, EvaluationContext evaluationContext, Class<T> clazz) {
        return getExpression(this.conditionCache, elementKey, conditionExpression).getValue(evaluationContext, clazz);
    }

    private Method getTargetMethod(Class<?> targetClass, Method method) {
        AnnotatedElementKey annotatedElementKey = new AnnotatedElementKey(method, targetClass);
        Method targetMethod = this.targetMethodCache.get(annotatedElementKey);
        if (targetMethod == null) {
            targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
            this.targetMethodCache.put(annotatedElementKey, targetMethod);
        }
        return targetMethod;
    }

}
复制代码

 

类:ExpressionRootObject定义,无脑使用即可

复制代码
public class ExpressionRootObject {

    private final Object object;

    private final Object[] args;

    public ExpressionRootObject(Object object, Object[] args) {
        this.object = object;
        this.args = args;
    }

    public Object getObject() {
        return object;
    }

    public Object[] getArgs() {
        return args;
    }
}
复制代码

 

三、Aspect定义

复制代码
import cn.hutool.core.util.IdUtil;
import com.auxgroup.adp.common.data.toolkit.LocalTenantContextHolder;
import com.auxgroup.adp.commons.utils.AuxJsonUtil;
import com.auxgroup.adp.commons.utils.AuxStringUtils;
import com.auxgroup.gcss.api.business.component.LocalTenantCode;
import com.auxgroup.gcss.api.business.component.utilt.ZoneIdUtil;
import com.auxgroup.gcss.api.business.dto.system.SysFieldChangeLogDto;
import com.auxgroup.gcss.api.business.intf.system.ISysFieldChangeLogService;
import com.auxgroup.gcss.server.business.component.aop.annotation.LogFieldChange;
import com.auxgroup.gcss.server.business.constant.BusinessConstant;
import org.apache.commons.beanutils.BeanUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.expression.EvaluationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;

/**
 * 字段变更日志记录切面
 *
 * @author zhuzhen
 */
@Aspect
@Component
public class LogFieldChangeAspect {

    private final static Logger LOGGER = LoggerFactory.getLogger(LogFieldChangeAspect.class);

    public static final ExpressionEvaluator<String> EVALUATOR = new ExpressionEvaluator<>();

    @Resource
    private ISysFieldChangeLogService sysFieldChangeLogService;


    /**
     * 配置切入点
     */
    @Pointcut("@annotation(com.auxgroup.gcss.server.business.component.aop.annotation.LogFieldChange)")
    public void pointCut() {
        // Do nothing
    }


    @AfterReturning(pointcut = "pointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        try {
            LogFieldChange logFieldChange = getAnnotationLog(joinPoint);
            if (logFieldChange == null) {
                return;
            }
            Object[] args = joinPoint.getArgs();
            if (args == null || args.length < 1) {
                return;
            }
            LOGGER.info("===========字典变更记录变更信息,args:{}", AuxJsonUtil.toString(args));
            List<String> fieldList = logFieldChange.bizType().getFieldList();
            String bizIdField = logFieldChange.bizIdField();
            String bizId = this.evalParam(joinPoint, bizIdField);
            String bizType = logFieldChange.bizType().getCode();
            String tenantCode = this.evalParam(joinPoint, logFieldChange.tenantCode());
if (AuxStringUtils.isBlank(bizId) || AuxStringUtils.isBlank(tenantCode)) {
                LOGGER.error("===================字段变更记录变更信息失败,bizId为空======bizType:{}", bizType);
                return;
            }
            String bizNo = this.evalParam(joinPoint, logFieldChange.bizNoField());
            String operator = this.evalParam(joinPoint, logFieldChange.operator());
            operator = AuxStringUtils.isNotBlank(operator) ? operator : BusinessConstant.SYSTEM;
            // 一个字段一个字段排查是否变更
            for (String field : fieldList) {
                String fieldValue = BeanUtils.getProperty(args[0], field);
                LOGGER.info("=========变更信息日志记录:bizType:{},bizId:{},field:{},fieldValue:{}", bizType, bizId, field, fieldValue);
                try {
                    saveAddFieldChangeLog(tenantCode, bizType, bizId, bizNo, field, fieldValue, operator, ZoneIdUtil.getCurrentTime());
                } catch (Exception e) {
                    LOGGER.error("===================字段变更记录变更信息异常:", e);
                }
            }
        } catch (Exception e) {
            LOGGER.info("获取变更字段并保存变更日志失败,异常信息:", e);
        }

    }

    private void saveAddFieldChangeLog(String tenantCode, String bizType, String bizId, String bizNo,
                                       String field, String fieldValue, String createBy, Date currentTime) {
        SysFieldChangeLogDto data = sysFieldChangeLogService.getNewFieldChangeLog(tenantCode, bizType, bizId, field).getData();
        if (data == null) {
            SysFieldChangeLogDto logDto = new SysFieldChangeLogDto();
            logDto.setLogId(IdUtil.fastSimpleUUID());
            logDto.setTenantCode(tenantCode);
            logDto.setBizType(bizType);
            logDto.setBizId(bizId);
            logDto.setBizNo(bizNo);
            logDto.setLogType(1);
            logDto.setChangeField(field);
            logDto.setChangeBefore(null);
            logDto.setChangeAfter(fieldValue);
            logDto.setCreateTime(currentTime);
            logDto.setUpdateTime(currentTime);
            logDto.setCreateBy(createBy);
            sysFieldChangeLogService.insertByDto(logDto);
        } else {
            String changeAfter = data.getChangeAfter();
            if (AuxStringUtils.equals(fieldValue, changeAfter)) {
                return;
            }
            data.setId(null);
            data.setLogId(IdUtil.fastSimpleUUID());
            data.setBizNo(bizNo);
            data.setLogType(2);
            data.setChangeBefore(changeAfter);
            data.setChangeAfter(fieldValue);
            data.setCreateTime(currentTime);
            data.setUpdateTime(currentTime);
            data.setCreateBy(createBy);
            data.setVersion(data.getVersion() + 1);
            sysFieldChangeLogService.insertByDto(data);
        }
    }


    /**
     * 获取存在的注解
     */
    private LogFieldChange getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(LogFieldChange.class);
        }
        return null;
    }

    private String evalParam(JoinPoint joinPoint, String elExpressKey) {
        if (AuxStringUtils.isBlank(elExpressKey)) {
            return null;
        }
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        Method method = ms.getMethod();
        Object[] args = joinPoint.getArgs();
        Object target = joinPoint.getTarget();
        Class<?> targetClass = target.getClass();
        EvaluationContext context = EVALUATOR.createEvaluationContext(target, targetClass, method, args);
        AnnotatedElementKey elementKey = new AnnotatedElementKey(method, targetClass);
        return EVALUATOR.condition(elExpressKey, elementKey, context, String.class);

    }

}
复制代码

 

四、使用

复制代码
@LogFieldChange(bizType = FieldChangeBizTypeEnum.SM_REPAIR_SETTLEMENT_STANDARD, bizIdField = "#dto.standardId",
        bizNoField = "#dto.standardNo", tenantCode = "#dto.tenantCode", operator = "#dto.createBy")
@Override
public AuxResponse<Boolean> insert(RepairSettlementStandardDto dto) {
    // do anyting      
}

@LogFieldChange(bizType = FieldChangeBizTypeEnum.SM_REPAIR_SETTLEMENT_STANDARD, bizIdField = "#dto.standardId",
        bizNoField = "#dto.standardNo", tenantCode = "#dto.tenantCode", operator = "#dto.createBy")
@Override
public AuxResponse<Boolean> update(RepairSettlementStandardDto dto) {
    // do anyting      
}


复制代码

 

posted on   十七年蝉  阅读(174)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Blazor Hybrid适配到HarmonyOS系统
· 万字调研——AI生成内容检测
· 解决跨域问题的这6种方案,真香!
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库
点击右上角即可分享
微信分享提示