1.创建一个日志记录表,这里就不细说了
2.需要封装一个注解
package com.kte.common.annotation;
import com.kte.common.enums.DictTypeEnum;
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FieldRecord {
String fieldName() default "";
String dateFormat() default "";
String[] whether() default {};
DictTypeEnum model() default DictTypeEnum.NULLDICT;
boolean isDictSplit() default false;
}
3.引入日志记录工具类`
package com.kte.common.utils;
import com.google.common.collect.Maps;
import com.kte.common.annotation.FieldRecord;
import com.kte.core.tool.utils.DateUtil;
import com.kte.core.tool.utils.Func;
import com.kte.core.tool.utils.ObjectUtil;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @description: 日志记录工具类
* @className: LogBeanUtils
**/
public class LogBeanUtils {
private static final String BIG_DECIMAL_NAME = "java.math.BigDecimal";
/**
* 获取变更内容
*
* @param newBean
* @param oldBean
*/
public static <T> String getChangedFields(T newBean, T oldBean) {
Field[] fields = newBean.getClass().getDeclaredFields();
StringBuilder builder = new StringBuilder();
for (Field field : fields) {
field.setAccessible(true);
if (field.isAnnotationPresent(FieldRecord.class)) {
try {
Object newValue = field.get(newBean);
Object oldValue = field.get(oldBean);
if (checkFields(builder, field, newValue, oldValue)) {
continue;
}
} catch (Exception e) {
e.printStackTrace();
System.out.println(e);
}
}
}
String context = StringUtils.replace(builder.toString(), "null", "");
return context;
}
/**
* 获取变更内容不记录空值
*
* @param newBean
* @param oldBean
* @param isRecordNull
* @return java.lang.String
*/
public static <T> String getChangedFields(T newBean, T oldBean, String isRecordNull) {
Field[] fields = newBean.getClass().getDeclaredFields();
StringBuilder builder = new StringBuilder();
boolean equals = "yes".equals(isRecordNull);
for (Field field : fields) {
field.setAccessible(true);
if (field.isAnnotationPresent(FieldRecord.class)) {
try {
Object newValue = field.get(newBean);
Object oldValue = field.get(oldBean);
if (checkFields(builder, field, newValue, oldValue, equals)) {
continue;
}
} catch (Exception e) {
e.printStackTrace();
System.out.println(e);
}
}
}
String context = StringUtils.replace(builder.toString(), "null", "");
return context;
}
public static <T> String getChangedFields(T newBean, T oldBean, Boolean isExcelChange) {
Field[] fields = newBean.getClass().getDeclaredFields();
StringBuilder builder = new StringBuilder();
for (Field field : fields) {
field.setAccessible(true);
if (field.isAnnotationPresent(FieldRecord.class)) {
try {
Object newValue = field.get(newBean);
Object oldValue = field.get(oldBean);
if (isExcelChange) {
if (ObjectUtil.isEmpty(newValue)) {
continue;
}
}
if (checkFields(builder, field, newValue, oldValue)) {
continue;
}
} catch (Exception e) {
e.printStackTrace();
System.out.println(e);
}
}
}
String context = StringUtils.replace(builder.toString(), "null", "");
return context;
}
private static boolean checkFields(StringBuilder builder, Field field, Object newValue, Object oldValue) {
if (ObjectUtil.isEmpty(newValue) && ObjectUtil.isEmpty(oldValue)) {
return true;
}
if (ObjectUtil.isEmpty(newValue)) {
newValue = null;
}
if (ObjectUtil.isEmpty(oldValue)) {
oldValue = null;
}
//处理BigDecimal类型的数据
String name = field.getType().getName();
if (name.equals(BIG_DECIMAL_NAME)) {
BigDecimal decimal = ObjectUtil.isEmpty(newValue) ? BigDecimal.ZERO : (BigDecimal) newValue;
BigDecimal decimal1 = ObjectUtil.isEmpty(oldValue) ? BigDecimal.ZERO : (BigDecimal) oldValue;
if (decimal.compareTo(decimal1) == 0) {
return true;
}
}
if (!Objects.equals(newValue, oldValue)) {
FieldRecord fieldRecord = field.getAnnotation(FieldRecord.class);
String[] whethers = fieldRecord.whether();
if (ObjectUtil.isNotEmpty(whethers) && whethers.length > 0) {
HashMap<String, String> dictMap = Maps.newHashMap();
//解析字典
if (ObjectUtil.isNotEmpty(fieldRecord.whether())) {
for (String whether : whethers) {
String[] s = whether.split("_");
dictMap.put(s[0], s[1]);
}
//获取字段名称
if (fieldRecord.isDictSplit()) {
String[] oldSplit = oldValue.toString().split(",");
String[] newSplit = newValue.toString().split(",");
String oldCollect = Arrays.stream(oldSplit).map(dictMap::get).collect(Collectors.joining(","));
String newCollect = Arrays.stream(newSplit).map(dictMap::get).collect(Collectors.joining(","));
setValue(builder, fieldRecord, oldCollect, newCollect);
dictMap.clear();
} else {
setValue(builder, fieldRecord, dictMap.get(oldValue.toString()), dictMap.get(newValue.toString()));
dictMap.clear();
}
return true;
}
}
Boolean x = getDate(builder, newValue, oldValue, fieldRecord);
if (x != null) {
return x;
}
//获取字段名称
builder.append(fieldRecord.fieldName());
builder.append(": 【更改前:");
builder.append(oldValue);
builder.append(", 更改后:");
builder.append(newValue);
builder.append("】;\n");
}
return false;
}
private static boolean checkFields(StringBuilder builder, Field field, Object newValue, Object oldValue, boolean isRecordNull) {
if (ObjectUtil.isEmpty(newValue) && ObjectUtil.isEmpty(oldValue)) {
return true;
}
if (ObjectUtil.isEmpty(newValue)) {
if (isRecordNull) {
return true;
}
}
if (ObjectUtil.isEmpty(oldValue)) {
oldValue = "";
}
//处理BigDecimal类型的数据
String name = field.getType().getName();
if (name.equals(BIG_DECIMAL_NAME)) {
BigDecimal decimal = (BigDecimal) newValue;
BigDecimal decimal1 = (BigDecimal) oldValue;
if (decimal.compareTo(decimal1) == 0) {
return true;
}
}
if (!Objects.equals(newValue, oldValue)) {
FieldRecord fieldRecord = field.getAnnotation(FieldRecord.class);
String[] whethers = fieldRecord.whether();
if (ObjectUtil.isNotEmpty(whethers) && whethers.length > 0) {
HashMap<String, String> dictMap = Maps.newHashMap();
//解析字典
if (ObjectUtil.isNotEmpty(fieldRecord.whether())) {
for (String whether : whethers) {
String[] s = whether.split("_");
dictMap.put(s[0], s[1]);
}
//获取字段名称
if (fieldRecord.isDictSplit()) {
String[] oldSplit = oldValue.toString().split(",");
String[] newSplit = newValue.toString().split(",");
String oldCollect = Arrays.stream(oldSplit).map(dictMap::get).collect(Collectors.joining(","));
String newCollect = Arrays.stream(newSplit).map(dictMap::get).collect(Collectors.joining(","));
setValue(builder, fieldRecord, oldCollect, newCollect);
dictMap.clear();
} else {
setValue(builder, fieldRecord, dictMap.get(oldValue.toString()), dictMap.get(newValue.toString()));
dictMap.clear();
}
return true;
}
}
Boolean x = getDate(builder, newValue, oldValue, fieldRecord);
if (x != null) {
return x;
}
//获取字段名称
builder.append(fieldRecord.fieldName());
builder.append(": 【更改前:");
builder.append(oldValue);
builder.append(", 更改后:");
builder.append(newValue);
builder.append("】;\n");
}
return false;
}
private static Boolean getDate(StringBuilder builder, Object newValue, Object oldValue, FieldRecord fieldRecord) {
String dateFormat = fieldRecord.dateFormat();
if (Func.isNotBlank(dateFormat)) {
if (oldValue instanceof Date) {
Date oldValue1 = (Date) oldValue;
Date newValue1 = (Date) newValue;
switch (dateFormat) {
case DateUtil.PATTERN_DATE:
setValue(builder, fieldRecord, DateUtil.format(oldValue1, DateUtil.PATTERN_DATE), DateUtil.format(newValue1, DateUtil.PATTERN_DATE));
break;
case DateUtil.PATTERN_DATETIME:
setValue(builder, fieldRecord, DateUtil.format(oldValue1, DateUtil.PATTERN_DATETIME), DateUtil.format(newValue1, DateUtil.PATTERN_DATETIME));
break;
default:
break;
}
} else if (ObjectUtil.isEmpty(oldValue) && ObjectUtil.isNotEmpty(newValue)) {
//处理老对象没有时间和新对象有时间问题
Date newValue1 = (Date) newValue;
switch (dateFormat) {
case DateUtil.PATTERN_DATE:
setValue(builder, fieldRecord, "", DateUtil.format(newValue1, DateUtil.PATTERN_DATE));
break;
case DateUtil.PATTERN_DATETIME:
setValue(builder, fieldRecord, "", DateUtil.format(newValue1, DateUtil.PATTERN_DATETIME));
break;
default:
break;
}
}
if (oldValue instanceof LocalDateTime) {
LocalDateTime oldValue1 = (LocalDateTime) oldValue;
LocalDateTime newValue1 = (LocalDateTime) newValue;
switch (dateFormat) {
case DateUtil.PATTERN_DATE:
setValue(builder, fieldRecord, DateUtil.format(oldValue1, DateUtil.PATTERN_DATE), DateUtil.format(newValue1, DateUtil.PATTERN_DATE));
break;
case DateUtil.PATTERN_DATETIME:
setValue(builder, fieldRecord, DateUtil.format(oldValue1, DateUtil.PATTERN_DATETIME), DateUtil.format(newValue1, DateUtil.PATTERN_DATETIME));
break;
default:
break;
}
} else if (ObjectUtil.isEmpty(oldValue) && ObjectUtil.isNotEmpty(newValue)) {
//处理老对象没有时间和新对象有时间问题
LocalDateTime newValue1 = (LocalDateTime) newValue;
switch (dateFormat) {
case DateUtil.PATTERN_DATE:
setValue(builder, fieldRecord, "", DateUtil.format(newValue1, DateUtil.PATTERN_DATE));
break;
case DateUtil.PATTERN_DATETIME:
setValue(builder, fieldRecord, "", DateUtil.format(newValue1, DateUtil.PATTERN_DATETIME));
break;
default:
break;
}
}
return true;
}
return null;
}
private static void setValue(StringBuilder builder, FieldRecord fieldRecord, String oldValue, String newValue) {
builder.append(fieldRecord.fieldName());
builder.append(": 【更改前:");
builder.append(oldValue);
builder.append(", 更改后:");
builder.append(newValue);
builder.append("】;\n");
}
}
测试
package com.kte.system.user.vo;
import com.kte.common.annotation.FieldRecord;
import com.kte.product.annotation.FieldMeta;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 对象实体类
*/
@Data
public class UserLogVo {
/**
* 用户姓名
*/
@ApiModelProperty(value = "用户姓名")
@FieldRecord(fieldName = "用户姓名")
private String realName;
/**
* 电子邮箱
*/
@ApiModelProperty(value = "电子邮箱")
@FieldRecord(fieldName = "电子邮箱")
private String email;
/**
* 手机号码
*/
@ApiModelProperty(value = "手机号码 ")
@FieldRecord(fieldName = "手机号码")
private String phone;
/**
* 用户性别
*/
@ApiModelProperty(value = "用户性别")
@FieldRecord(fieldName = "用户性别", whether = {"1_男", "2_女"})
private String sex;
}
//调用工具类,获取变更的内容
String change=LogBeanUtils.getChangedFields(T newBean, T oldBean, String isRecordNull);
把变更的内容记录入库,此位置省略入库操作,自行编写
//注意细节:
1. @FieldRecord:需要记录变更得字段必须加上此注解
2. whether:枚举转换,如性别,我们入库记录的是“1和2”,记录日志时可自动装换为“男和女”