记录Java对象修改前和修改后的变化

发布时间 2023-11-25 10:08:21作者: 爱学习的疯倾

一、记录跟变信息对象

/**
 * @author FengQing
 * @program yf-client
 * @description
 * @date 2023/11/01
 */
@Getter
@Setter
@ToString
public class ChangePropertyMsg {
    /**
     * 变更信息
     */
    private String changeMsg;
    /**
     * 变更属性集合
     */
    private List<String> properties;
}

 

二、工具类(传入两个相同类型的对象,对比属性得到修改信息)

提示:监听属性变化是通过【ApiModelProperty】注解,当然你也可以通过自定义注解实现;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import com.yf.client.entity.log.ChangePropertyMsg;
import com.yf.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang.ObjectUtils;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * @author FengQing
 * @program yf-client
 * @description
 * @date 2023/11/01
 */
@Slf4j
public class BeanChangeUtil<T> {

    /**
     * 传入两个相同类型的对象,对比属性得到修改信息
     * @param oldBean
     * @param newBean
     * @return 属性修改信息
     */
    public static <aClass> String getChangeInfo(Object oldBean, Object newBean){
        Class aClass = oldBean.getClass();
        BeanChangeUtil<aClass> t = new BeanChangeUtil<>();
        ChangePropertyMsg cfs = t.contrastObj(oldBean, newBean);
        if (StringUtils.isNotEmpty(cfs.getChangeMsg())) {
            return cfs.getChangeMsg();
        }
        return null;
    }
    /**
     * 传入两个相同类型的对象,对比属性得到修改信息
     * @param oldBean
     * @param newBean
     * @return **完整属性修改信息**
     */
    public ChangePropertyMsg contrastObj(Object oldBean, Object newBean) {
        // 转换为传入的泛型T
        T oldPojo = (T) oldBean;
        // 通过反射获取类型及字段属性
        Field[] fields = oldPojo.getClass().getDeclaredFields();
        return jdk8OrAfter(Arrays.asList(fields), oldPojo, (T) newBean);
    }

    // lambda表达式,表达式内部的变量都是final修饰,需要传入final类型的数组
    private ChangePropertyMsg jdk8OrAfter(List<Field> fields, T oldBean, T newBean) {
        ChangePropertyMsg cf = new ChangePropertyMsg();
        // 创建字符串拼接对象
        StringBuilder str = new StringBuilder();
        List<String> fieldList = new ArrayList<>();
        fields.forEach(field -> {
            field.setAccessible(true);
            if (field.isAnnotationPresent(ApiModelProperty.class)) {
                try {
                    // 获取属性值
                    Object newValue = field.get(newBean);
                    Object oldValue = field.get(oldBean);
                    if (StringUtils.isNotNull(newValue)) {
                        if (ObjectUtils.notEqual(oldValue, newValue)) {
                            boolean isOldValueArray = ArrayUtil.isArray(oldValue);
                            boolean isNewValueArray = ArrayUtil.isArray(newValue);

                            if (isOldValueArray && isNewValueArray) {
                                Object[] oldArray = (Object[]) oldValue;
                                Object[] newArray = (Object[]) newValue;
                                if (!Arrays.deepEquals(oldArray, newArray)) {
                                    fieldList.add(field.getName());
                                    str.append(field.getAnnotation(ApiModelProperty.class).value() + ":");
                                    str.append("修改前=【" + Arrays.toString(oldArray) + "】,修改后=【" + Arrays.toString(newArray) + "】;\n");
                                }
                            } else {
                                fieldList.add(field.getName());
                                str.append(field.getAnnotation(ApiModelProperty.class).value() + ":");
                                str.append("修改前=【" + formatPropertyValue(oldValue) + "】,修改后=【" + formatPropertyValue(newValue) + "】;\n");
                            }
                        }
                    }
                } catch (Exception e) {
                    log.error("比对Bean属性是否变化失败,", e);
                }
            }
        });
        cf.setChangeMsg(str.toString());
        cf.setProperties(fieldList);
        return cf;
    }

    /**
     * 时间处理
     * @param value
     * @return
     */
    private static Object formatPropertyValue(Object value) {
        if (value instanceof Date) {
            return DateUtil.format((Date) value, "yyyy-MM-dd");
        } else if (value instanceof LocalDateTime) {
            LocalDateTime localDateTimeValue = (LocalDateTime) value;
            return localDateTimeValue.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        }
        return value;
    }
}

 

三、在修改业务中使用步骤(我这边使用的mybatis-plus,其它的同理方式处理)

 /**
     * 更新操作(业务层)
     * @param channel
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class, RuntimeException.class, MybatisPlusException.class})
    public void updateChannel(ChannelParam channel) {
        // 1、查询修改前的数据
        ChannelVo channelVo = this.selectChannelById(channel.getId());
     // 2、修改前的对象跟前端返回的对象不一样如:前端传来的“ChannelParam”,修改前是“ChannelVo”对象,把它复制到统一对象中 ChannelParam oldParam
= new ChannelParam(); BeanUtil.copyProperties(channelVo, oldParam); Channel data = new Channel;
     BeanUtil.copyProperties(channel, data);
super.updateById(data); // 记录日志对象 ChannelLogParam logParam = new ChannelLogParam();

     // 调用监听属性变化工具类 BeanChangeUtil<ChannelParam> t = new BeanChangeUtil<>(); ChangePropertyMsg cfs = t.contrastObj(oldParam, channel); if (StringUtils.isNotBlank(cfs.getChangeMsg())) {
        logParam.setUpdateObj(cfs.getProperties().toString()); logParam.setUpdateMsg(cfs.getChangeMsg()); }      // 添加日志记录 channelLogService.insertChannelLogging(
logParam); }

 

四、数据库存储效果如下图:

 

到这里就大功告成了。