aop实现前后数据对比的操作记录

编程入门 行业动态 更新时间:2024-10-12 08:22:41

aop实现前后数据对比的<a href=https://www.elefans.com/category/jswz/34/1770947.html style=操作记录"/>

aop实现前后数据对比的操作记录

aop实现前后数据对比的操作记录

先看效果

定义注解

CoverItem:这个注解用于修饰属性,目的是保存记录时将字段的英文转成注解中的value值,也就是转成中文

import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CoverItem {String value();boolean isCheck() default true;}

OperLog:这个注解作为切入点

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperLog {int type();Class clazz();Class convertClass();}

aspectj业务层

逻辑不难直接上代码了,实现就是查询未修改之前的对象和修改之后的对象,通过对比两个对象的field,判断是否修改值,再将修改的值封装成一条日志保存入库。
这里考虑到我的业务所以并没有做通用的全覆盖,需要用的话,参考着改一下应该可以很轻松的应用到自己的业务场景中。
aop环向切面

import cn.flydiy.cloud.base.context.User;
import cn.flydiy.cloudmon.lang.StringUtils;
import cn.flydiy.cloudmon.utils.SecurityUtils;
import cn.hutool.extra.spring.SpringUtil;
import com.flydiy.sample.auto.dao.OperatorLogMapper;
import com.flydiy.sample.auto.pojo.po.OperatorLogPO;
import com.flydiy.sample.ext.aspectj.annotation.CoverItem;
import com.flydiy.sample.ext.aspectj.annotation.OperLog;
import com.flydiy.sample.ext.convert.CustomerConvert;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;@Aspect
@Component
public class OperatorLogAspectj {@Pointcut("@annotation(com.flydiy.sample.ext.aspectj.annotation.OperLog)")public void pointcut(){}/*** 操作日志记录切面* @param point* @throws Exception*/@Around("pointcut()")public void operatorLog(ProceedingJoinPoint point) throws Exception {Object obj = getArg(point);int type = getType(point);Class clazz = getClazz(point);Class convertClass = getConvertClass(point);Long originId = getId(obj);Object originObj = getObj(clazz,convertClass,originId);try {point.proceed();} catch (Throwable e) {throw new RuntimeException(e);}Object updateObj = getObj(clazz,convertClass,originId);//构建日志对象OperatorLogPO operatorLog = createOperLog(originObj,updateObj,type,originId);if(Objects.isNull(operatorLog)) {return;}//保存日志saveLog(operatorLog);}private Class getClazz(ProceedingJoinPoint point) {OperLog log = getLog(point);return log.clazz();}private Class getConvertClass(ProceedingJoinPoint point) {OperLog log = getLog(point);return log.convertClass();}private int getType(ProceedingJoinPoint point) {OperLog log = getLog(point);return log.type();}private OperLog getLog(ProceedingJoinPoint point) {OperLog log = getAnnotationLog(point);if(Objects.isNull(log)) {throw new RuntimeException("接口缺少OperLog注解");}return log;}private void saveLog(OperatorLogPO operatorLog) {OperatorLogMapper mapper = SpringUtil.getBean(OperatorLogMapper.class);mapper.insert(operatorLog);}private OperatorLogPO createOperLog(Object originObj, Object updateObj, int type, Long originId) throws IllegalAccessException {StringJoiner sj = new StringJoiner("|");Class<?> clazz = originObj.getClass();Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {field.setAccessible(true);Object originField = getField(field,originObj);Object updateField = getField(field,updateObj);//如果属性变更,则记录if(!originField.equals(updateField)) {CoverItem coverItem = field.getAnnotation(CoverItem.class);isValidCoverItem(coverItem,field);if(!coverItem.isCheck()) {continue;}String desc = createLogDesc(field,originField,updateField);sj.add(desc);}}if(StringUtils.isBlank(sj.toString())) {return null;}OperatorLogPO operatorLog = buildOperLog(sj);operatorLog.setType(type);operatorLog.setOriginId(originId);return operatorLog;}private Object getField(Field field,Object originObj) throws IllegalAccessException {Object obj = field.get(originObj);if(Objects.isNull(obj)) {obj = "null";}return obj;}private OperatorLogPO buildOperLog(StringJoiner sj) {User user = SecurityUtils.getLoginUser().get();if(Objects.isNull(user)) {throw new RuntimeException("获取用户信息异常");}String username = user.getUsername();String userId = user.getId();String desc = sj.toString();OperatorLogPO operator = new OperatorLogPO();operator.setOperatorLog(desc);operator.setUesrName(username);operator.setCreatedBy(userId);operator.setCreatedDate(new Date());return operator;}private String createLogDesc(Field field, Object originField, Object updateField) {CoverItem annotation = field.getAnnotation(CoverItem.class);String desc = "将字段[%s],从[%s]变更为[%s]";desc = String.format(desc,annotation.value(),originField,updateField);return desc;}private void isValidCoverItem(CoverItem coverItem, Field field) {if(Objects.isNull(coverItem)) {throw new RuntimeException(field.getName()+"缺少@CoverItem注解");}}private Object getObj(Class clazz, Class convertClass, Long id) throws Exception {Object obj = SpringUtil.getBean(clazz);Method method = clazz.getMethod("selectById", Serializable.class);Object invoke = method.invoke(obj, id);Object convert = CustomerConvert.convert(invoke, convertClass);return convert;}private Long getId(Object obj) throws Exception {Field field = obj.getClass().getDeclaredField("id");field.setAccessible(true);Long id = (Long) field.get(obj);return id;}private Object getArg(ProceedingJoinPoint point) {Object[] args = point.getArgs();if(Objects.isNull(args) || args.length < 1) {throw new RuntimeException("参数异常");}return args[0];}private OperLog getAnnotationLog(ProceedingJoinPoint point) {Signature signature = point.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if(Objects.nonNull(method)) {return method.getAnnotation(OperLog.class);}return null;}}

其中用到的工具类,po2po工具类

import com.flydiy.sample.auto.pojo.po.ColorSamplePO;
import com.flydiy.sample.auto.pojo.po.SampleInfoPO;
import com.flydiy.sample.auto.pojo.vo.ColorSampleExportVO;
import com.flydiy.sample.auto.pojo.vo.SampleInfoExportVO;
import com.flydiy.sample.ext.pojo.vo.ColorSampleExtExportVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Objects;public class CustomerConvert {/*** 类型转换* @param obj* @param clazz* @return* @throws Exception*/public static Object convert(Object obj, Class clazz) throws Exception {Object targetObj = clazz.getDeclaredConstructor().newInstance();Class<?> originClass = obj.getClass();Field[] fields = originClass.getDeclaredFields();for (Field field : fields) {if(notValidField(field) || isStaticModifier(field)) {continue;}field.setAccessible(true);Object fileAtr = field.get(obj);Field field1 = null;try {field1 = clazz.getDeclaredField(field.getName());} catch (NoSuchFieldException e) {continue;}if(notValidField(field1)) {continue;}field1.setAccessible(true);field1.set(targetObj,fileAtr);}return targetObj;}private static boolean notValidField(Field field) {return Objects.isNull(field);}private static boolean isStaticModifier(Field field) {return Modifier.isStatic(field.getModifiers());}}

使用

在接口加上@OperLog注解

@ApiOperation("单条更新")@PostMapping({"/api/v1/sample-info/ext/update", Constant.INNER_PATH_PREFIX + "/api/v1/sample-info/ext/update"})@OperLog(type = OperatorTypeConstant.SAMPLEINFOTYPE, clazz = SampleInfoMapper.class, convertClass = SampleInfoExtPO.class)public ResponseInfo<Boolean> update(@RequestBody SampleInfoExtPO sampleInfoExtPO) throws Exception {// 验证入参ParameterSupport.validateParameter(sampleInfoExtPO);// 验证表单是否完整ParameterSupport.validateForm(sampleInfoExtPO);// 格式化入参ParameterSupport.formatParameter(sampleInfoExtPO);SampleInfoPO sampleInfoPO = (SampleInfoPO) CustomerConvert.convert(sampleInfoExtPO, SampleInfoPO.class);// 调用Serviceboolean flag = sampleInfoExtService.updateById(sampleInfoPO);return ResponseInfo.<Boolean>success().data(flag);}

状态的日志记录是 [将发布状态从1更新为2] 这种形式,看起来很不直观,
如果能达到 [将发布状态从未发布 更新为 已发布] 这种效果就好了。

那么怎么才能解决呢。当然是有办法的啦:

我们可以在我们的自定义枚举中加一个字段fieldCover,标识数字对应的状态值。

import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CoverItem {String value();boolean isCheck() default true;String fieldCover();}

这个字段的值我们可以写 数字对应的中文的json字符串。
例如:

public class Person{@CoverItem(fieldCover="{\n" +"    \"1\": \"未发布\",\n" +"    \"2\": \"已发布\"\n" +"}")private Integer status;
}

然后在获取field字段时判断有没有这个注解修饰,并且是否有fieldCover值,如果有这个属性值,直接用JSONUtil工具转成JsonObject对象,然后将field字段的值转为对应的中文。
代码我就不写了,如果实在有需要的话,可以评论区留言。

最后,如果本文对你有一点点的帮助的话,请帮忙点点赞哦,当然要是能顺便点个关注的话,那up会更开心的 闪闪发光(o゜▽゜)o☆

更多推荐

aop实现前后数据对比的操作记录

本文发布于:2024-03-07 01:17:14,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1716469.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:操作   数据   aop

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!