追溯功能(即操作日志记录每个属性编辑前后的变化)

        最近有个新需求,就是要实现某个任务的追溯功能,就是这个任务的任何属性对应的值在编辑前后都需要记录下,记录下是谁在那个时间修改了该任务的什么值。

        我的需求是在长链接的时候,某人修改了任务的某个属性需要实时通知其他在线人员,所以我需要在修改任务,删除任务,新增任务这个三个长链接里面添加操作记录,记录新增,删除,修改的操作,并且修改又包括修改任务进度,修改任务名称,修改任务状态等。

原文链接:

操作日志应记录编辑的前后内容变化_java记录修改前后信息-CSDN博客
首先感谢原文作者,在原有代码做了一点点小的修改,作为记录。直接贴代码

一.表结构设计

主键id,任务id,操作者,操作详情(值的前后变化或者删除或者新增),操作类型(新增任务,删除任务,修改任务)

二.总体思路是增加一个注解类,将注解加到要进行记录变化的Java类属性上却可。

1. 实现注解类: 

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldName {
 
    String value();
 
    boolean isIgnoreNull() default false;
}

2. 将注解加到Java类需要记录修改的属性

@ApiModelProperty(value = "任务名称")
@FieldName("任务名称")
private String taskName;

@ApiModelProperty(value = "该任务的负责人")
@FieldName("该任务的负责人")
private Long taskUserId;

@ApiModelProperty(value = "优先级")
@FieldName("优先级")
private String priority;

@FieldName("任务进度")
private Integer taskProgress;

3. 写一个LogUtil类,对新旧对象进行比较,将变化的内容记录下来,返回List<String>,为了防止异常出现影响正常的业务,用try-catch进行异常处理。

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.piesat.space.project.socket.annotation.FieldName;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
public class LogUtil {

    private static  SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
    /**
     * 记录修改信息
     * @param newObj 当前最新的数据,将要update到数据库的数据
     * @param oldObj 更新前数据, 一般是已经保存到数据库的数据,在update前从数据库查询获得
     * @param clazz
     * @return
     */
    public static List<String> getUpdateContentList(Object newObj, Object oldObj, Class clazz) {
        List<String> contentList = new ArrayList<>();
        if (newObj == null || oldObj == null) {
            return contentList;
        }

        try {
            //通过hutool BeanUtil工具将实体类转换为Map
            Map newMap = BeanUtil.beanToMap(newObj);
            Map oldMap = BeanUtil.beanToMap(oldObj);

            //通过类对象获取类字段
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                //判断是否有FieldName注解
                if (field.isAnnotationPresent(FieldName.class)) {
                    FieldName fieldName = field.getAnnotation(FieldName.class);
                    //空的和忽略的字段不进行处理
                    if (fieldName.isIgnoreNull() &&  ObjectUtil.isEmpty(newMap.get(field.getName()))) {
                        continue;
                    }
                    String newValue =  newMap.get(field.getName()) == null ? "" : newMap.get(field.getName()).toString();
                    String oldValue =  oldMap.get(field.getName()) == null ? "" : oldMap.get(field.getName()).toString();
                    if(field.getType() == Date.class){
                        try{
                            if(StringUtils.isNotEmpty(newValue)){
                                newValue = formatter.format(newMap.get(field.getName()));
                            }
                            if(StringUtils.isNotEmpty(oldValue)){
                                oldValue = formatter.format(oldMap.get(field.getName()));
                            }
                        }catch (Exception e){
                            e.printStackTrace();
                        }
                    }
                    String changeField;
                    Map<String, String> conMap = new HashMap<>();
                    //新旧记录内容不同,说明是修改过,因此记录起来
                    if (!newValue.equals(oldValue)) {
                        //CHANGE_TEXT 自定义拼接内容
                        //CHANGE_TEXT = "fieldName-oldValue --> 调整为 --> fieldName-newValue"
                        String CHANGE_TEXT =  "fieldName:oldValue --> newValue";
                        changeField = CHANGE_TEXT.replaceAll("fieldName", fieldName.value())
                                .replaceAll("oldValue", oldValue)
                                .replaceAll("newValue", newValue);
                        log.info(changeField);
                        contentList.add(changeField);
                    }
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return contentList;
    }
}

4.一切准备就结果,接下来就是具体使用:

我的调用方式:

 /**
     * 获取编辑前后的变化内容
     */
    public List<String> getUpdateTaskList(SpaceManageTask task) {
        // product代表新数据,将要被update到数据库中
        // oldObject 是数据库中的数据,用以跟新的数据进行比较
        SpaceManageTask oldTask = taskMapper.getTaskById(task.getSpaceManageTaskId());
        return LogUtil.getUpdateContentList(task, oldTask, SpaceManageTask.class);
    }
        //记录修改操作
        List<String> updateContentList =taskService.getUpdateTaskList(task);
        String updateContent = "修改任务";
        if(!updateContentList.isEmpty()){
            updateContent += ": "+updateContentList;
        }

        // 日志记录的service类新建一条更新日志
        SpaceManageHistory history = new SpaceManageHistory();
        history.setSpaceManageHistoryId(SnowflakeIdWorker.generateId());
        history.setSpaceManageTaskId(task.getSpaceManageTaskId());
        history.setSpaceManageMilestoneId(task.getSpaceManageMilestoneId());
        history.setSpaceManageProjectId(task.getSpaceManageProjectId());
        history.setOperator(spaceManageProjectDto.getOperatorId());
        history.setOperateType("编辑");
        history.setDetail(updateContent);

        //基础字段赋值
        history.setInsertTime(LocalDateTime.now());
        history.setUpdateTime(LocalDateTime.now());
        history.setInsertedBy(spaceManageProjectDto.getHhxsUserId());
        history.setUpdatedBy(spaceManageProjectDto.getHhxsUserId());
        history.setVersion("3.0.0");
        history.setLogicDelete(0);
        historyService.saveHistoryRecord(history);

        //修改项目名称
        taskService.updateTask(task);
     
         ...长链接操作

原作者的调用方式:

在Controller类的edit请求中:

// 某个Controller的edit方法中, 此处的product是新数据,一般是前端传回来的数据
List<String> updateContentList = getUpdateContentList(product);
String updateContent = "修改产品";
if(!updateContentList.isEmpty()){
     updateContent += ": "+updateContentList;
}
// 日志记录的service类新建一条更新日志
updateRecordService.setProductUpdateRecord(product.getId(), "编辑", updateContent);
 
/**
  * 获取编辑前后的变化内容
*/
 private List<String> getUpdateContentList(Product product){
     // product代表新数据,将要被update到数据库中   
     // oldObject 是数据库中的数据,用以跟新的数据进行比较
     Product oldObject = productService.getById(product.getId());
     return LogUtil.getUpdateContentList(product, oldObject, Product .class);
}

最后的效果如图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值