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

一、记录跟变信息对象
package com.yf.client.entity.log;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.yf.auth.client.util.StpLoginUserUtil;
import com.yf.utils.StringUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

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

    /**
     * 变更信息(JSON数组)
     */
    private JSONArray changeList;

    public ChangePropertyMsg() {
        this.changeList = new JSONArray();
    }

    public void addChange(String fieldName, Object oldValue, Object newValue, String attribute) {
        JSONObject changeObject = new JSONObject();
        changeObject.put("fieldName", fieldName);
        changeObject.put("attribute", attribute);
        if (StringUtils.isNotEmpty(oldValue)) {
            changeObject.put("oldBean", oldValue);
        } else {
            changeObject.put("oldBean", "");
        }
        changeObject.put("newBean", newValue);
        changeObject.put("updateTime", formatLocalDateTime(LocalDateTime.now()));
        changeObject.put("userName", StpLoginUserUtil.getLoginUser().getNickname());

        changeList.add(changeObject);
    }

    private String formatLocalDateTime(LocalDateTime localDateTime) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        return localDateTime.format(formatter);
    }

    public JSONArray getChangeList() {
        return changeList;
    }
}

 

 

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

提示:监听属性变化是通过【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");
                                    cf.addChange(field.getAnnotation(ApiModelProperty.class).value(),
                                            Arrays.toString(oldArray),
                                            Arrays.toString(newArray));
                                }
                            } else {
                                fieldList.add(field.getName());
                                str.append(field.getAnnotation(ApiModelProperty.class).value() + ":");
                                str.append("修改前=【" + formatPropertyValue(oldValue) + "】,修改后=【" + formatPropertyValue(newValue) + "】;\n");
                                cf.addChange(field.getAnnotation(ApiModelProperty.class).value(),
                                        formatPropertyValue(oldValue),
                                        formatPropertyValue(newValue));
                            }
                        }
                    }
                } 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());

       logParam.setJsonMsg(cfs.getChangeList().toString());
      }      

    // 添加日志记录
    channelLogService.insertChannelLogging(
logParam);
}

 

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

 

到这里就大功告成了。

 

posted @ 2023-11-25 10:03  爱学习的疯倾  阅读(826)  评论(0编辑  收藏  举报