1.先写个注解,加在entity的字段上,标记要记录这个字段的更新记录,再加个变更记录表(你们可以用自己的日志表)
@Retention(RetentionPolicy.RUNTIME)
@Target({java.lang.annotation.ElementType.FIELD})
@Documented
public @interface ModifyAware {
/**
* 字段名称,这里直接写,不去找其他注解或者解析doc了,必填
*/
public abstract String name();
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("modifyrecord")
@ApiModel("变更记录表")
public class ModifyRecord extends CommonBaseEntity {
private static final long serialVersionUID = 7314951651619250712L;
/**
* 变更表名
*/
@TableField("tableName")
@ApiModelProperty("变更表名/业务名称")
private String tableName;
/**
* 变更记录Id
*/
@TableField("recordKey")
@ApiModelProperty("变更记录Id")
private Integer recordKey;
/**
* 变更字段
*/
@TableField("modifyColumn")
@ApiModelProperty("变更字段")
private String modifyColumn;
/**
* 变更前内容
*/
@ApiModelProperty("变更前内容")
@TableField("beforeContent")
private String beforeContent;
/**
* 变更后内容
*/
@TableField("afterContent")
@ApiModelProperty("变更后内容")
private String afterContent;
/**
* 变更时间
*/
@TableField("modifyTime")
@ApiModelProperty("变更时间")
private Date modifyTime;
/**
* 变更原因
*/
@TableField("modifyReason")
@ApiModelProperty("变更原因")
private String modifyReason;
/**
* 变更人
*/
@TableField("modifier")
@ApiModelProperty("变更人")
private String modifier;
/**
* 变更人姓名
*/
@TableField(exist = false)
@ApiModelProperty("变更人姓名")
private String modifierName;
public ModifyRecord(String tableName, Integer recordKey, String modifyColumn, String beforeContent, String afterContent, Date modifyTime, String modifyReason, String modifier) {
this.tableName = tableName;
this.recordKey = recordKey;
this.modifyColumn = modifyColumn;
this.modifyTime = modifyTime;
this.beforeContent = beforeContent;
this.afterContent = afterContent;
this.modifyReason = modifyReason;
this.modifier = modifier;
}
}
2.再在BaseService和impl实现中增加modify方法(如果没有就自己写,基本上用mybatis都会自己包一层service(继承IService)服务,多定义一些好用的增删改查的方法),等同update,实现变更记录的逻辑,最好前端增加一个变更原因字段,带到entity里面,做变更原因记录,我这里先写死;
PS:这里反射取值时,直接穷举了字段基本类型,常规开发就这些,暂不支持list,map等集合的变化,有需要自己多写个if分支拓展下。
public int modify(T entity) {
Integer id = (Integer) EntityUtils.getFieldValueByFieldName("id", entity);
//获取旧数据
T old = this.getById(id);
//执行更新,保证业务执行
int num = getBaseMapper().updateById(entity);
//获取加了注解的字段
Field[] fields = entity.getClass().getDeclaredFields();
String className = entity.getClass().getSimpleName();
List<ModifyRecord> modifyRecordList = Lists.newArrayList();
for (Field field : fields) {
field.setAccessible(true);
if (!field.isAnnotationPresent(ModifyAware.class)) {
continue;
}
ModifyAware attr = field.getAnnotation(ModifyAware.class);
//获取实体旧值
Object oldValue = EntityUtils.getFieldValueByFieldName(field.getName(), old);
//获取新值
Object newValue = EntityUtils.getFieldValueByFieldName(field.getName(), entity);
//新旧比较
String beforeContent = "";
String afterContent = "";
if (null == oldValue && null == newValue) {
continue;
} else if (null == oldValue) {
afterContent = getFieldValue(field, newValue);
} else if (null == newValue) {
beforeContent = getFieldValue(field, oldValue);
} else {
beforeContent = getFieldValue(field, oldValue);
afterContent = getFieldValue(field, newValue);
}
if (!beforeContent.equals(afterContent)) {//前后不一致
//保存变更
modifyRecordList.add(new ModifyRecord(className, id, attr.name(), beforeContent, afterContent, new Date(), "信息更新", LoginUtil.getLoginName()));
}
}
//无则直接返回
if (CollectionUtils.isEmpty(modifyRecordList)) {
return num;
}
//有直接存储
ModifyRecordMapper modifyRecordMapper = SpringContextUtil.getBean(ModifyRecordMapper.class);
modifyRecordMapper.insertBatch(modifyRecordList);
return num;
}
/**
* 获取字段值,并转成string
*
* @param field 字段
* @param value value
* @return 字符串类型的值
*/
private static String getFieldValue(Field field, Object value) {
String typeName = field.getType().getSimpleName();
if ("Integer".equalsIgnoreCase(typeName)) {//数字类型
return String.valueOf(value);
} else if ("BigInteger".equalsIgnoreCase(typeName)) {//数字类型
return String.valueOf(value);
} else if ("Short".equalsIgnoreCase(typeName)) {//数字类型
return String.valueOf(value);
} else if ("Float".equalsIgnoreCase(typeName)) {//数字类型
return String.valueOf(value);
} else if ("Double".equalsIgnoreCase(typeName)) {//数字类型
return String.valueOf(value);
} else if ("Long".equalsIgnoreCase(typeName)) {//数字类型
return String.valueOf(value);
} else if ("BigDecimal".equalsIgnoreCase(typeName)) {//数字类型
return ((BigDecimal) value).toPlainString();
} else if ("String".equalsIgnoreCase(typeName)) {//字符串
return value.toString();
} else if ("Date".equalsIgnoreCase(typeName)) {//日期
SimpleDateFormat sdf = new SimpleDateFormat(DatePattern.YYYY_MM_DD_HH_MM_SS);
return sdf.format(value);
}
return "";
}
3.最后在业务数据中展示这些变更日志时,可能还要在查询时转换下,必须存储的项目Id,实际日志要展示项目中文,那就在日志列表的接口中自己识别转换下,最后效果如下: