数据业务级日志记录业务操作前后数据(springBoot)续

    关于这个级别的日志,上次讲了2个方案:

    1、aop切面,使用环绕事件,在proceed()前后分别处理,组织操作前后的参数

    2、提供公用的工具类方法,开启线程处理

    今天主要是再补充下方案1,方案一其实可以增加一个声明注解,接口方法上注解,描述方法的具体作用。看硬货:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OperateLogAnno {
    /**
     * 日志描述
     *
     * @return
     */
    String logDesc();
}

增加一个上面这样的声明类。然后方法名上注解:

@OperateLogAnno(logDesc = "操作描述说明")

剩下就是aop切面类了:

@Around("addPointcut() || updatePointcut()")
public Object Around(ProceedingJoinPoint pjp) throws Throwable {
    Signature sig = pjp.getSignature();
    if (!(sig instanceof MethodSignature)) {
        throw new IllegalArgumentException("该注解只能用于方法");
    }
    MethodSignature msig = (MethodSignature) sig;
    Object target = pjp.getTarget();
    Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
    // 类名
    String targetName = target.getClass().getSimpleName();
    // 方法名
    String methodName = currentMethod.getName();
    Object[] args = pjp.getArgs();
    // 方法的第1个参数
    Object model = args[0];
    Class modelClass = model.getClass();
    // 查询ID
    Integer id = null;
    // 需要通过反射获取的Mapper类
    Mapper mapper = null;
    // 更新前数据行
    String beforeJson = "{}";
    // 更新后数据行
    String afterJson;

    // 是否要记录数据行日志
    boolean need = false;
    // 是否是新增操作,默认为新增
    boolean isAdd = true;
    OperateLogAnno anno = currentMethod.getAnnotation(OperateLogAnno.class);
    if (anno != null) {
        need = true;
    }
    if (need) {
        // 获取查询ID
        Method getId = modelClass.getMethod("getId");
        id = Integer.class.cast(getId.invoke(model));
        if (id != null && id > 0) {
            isAdd = false; // 修改操作
        }
        targetName = targetName.substring(0, targetName.indexOf("ServiceImpl"));
        String mapperName = (new StringBuilder()).append(Character.toLowerCase(targetName.charAt(0)))
                .append(targetName.substring(1)).toString() + "Mapper";
        mapper = BeanHeader.getBean(mapperName);
    }

    if (need) {
        Object before;
        // 修改操作,先取出未修改的数据行
        if (!isAdd) {
            before = mapper.selectByPrimaryKey(id);
        } else {
            before = "{}";
        }
        beforeJson = JSON.toJSONString(before);
    }

    // 执行原始方法
    Object object = pjp.proceed();

    Result result = (Result) object;
    // 返回值异常,直接返回结果
    if (result.getCode() != ResultCode.SUCCESS.code()) {
        return result;
    }

    if (need) {
        // 新增操作,从返回结果中获取数据行id
        if (isAdd) {
            Result addResult = (Result) object;
            id = addResult.getData();
        }
        if (id != null && id > 0) {
            Object after = mapper.selectByPrimaryKey(id);
            afterJson = JSON.toJSONString(after);

            Class clazz = after.getClass();
            // 机构ID
            Integer orgId = 0;
            try {
                Method getOrgId = clazz.getMethod("getOrgId");
                orgId = Integer.class.cast(getOrgId.invoke(after));
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                log.info("对象{}没有方法getOrgId,设置默认值", clazz.getSimpleName());
            }
            // 项目ID
            Integer subjectId = 0;
            try {
                Method getSubjectId = clazz.getMethod("getSubjectId");
                subjectId = Integer.class.cast(getSubjectId.invoke(after));
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                log.info("对象{}没有方法getSubjectId,设置默认值", clazz.getSimpleName());
            }
            // 创建者ID
            Integer createBy = BusinessConstant.CREATE_BY_DEFAULT;
            try {
                Method getCreateBy = modelClass.getMethod("getCreateBy");
                createBy = Integer.class.cast(getCreateBy.invoke(model));
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                log.info("对象{}没有方法getCreateBy设置默认值", clazz.getSimpleName());
            }
            // 更新者ID
            Integer updateBy = BusinessConstant.CREATE_BY_DEFAULT;
            try {
                Method getUpdateBy = modelClass.getMethod("getUpdateBy");
                updateBy = Integer.class.cast(getUpdateBy.invoke(model));
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                log.info("对象{}没有方法getUpdateBy设置默认值", clazz.getSimpleName());
            }
            //记录数据业务级日志
            OperateLogUtil.sendOperateLog(afterJson, beforeJson, orgId, subjectId, targetName, anno.logDesc(),
                    methodName, isAdd ? createBy : updateBy);
        }
    }

    return result;
} 
  

上面这个方法是以Object object = pjp.proceed();这行为界,前面组织操作前的json字符串,后面组织操作后的json字符串。当然都是通过反射获取对应的mapper,从参数里取id,然后查询,组织参数。保存依然是调用的开启线程保存日志的公共方法。

到这里就没有什么其他可说的了,关键点都跟大家分享了。下面总结下这个方案的优劣吧。

1、在proceed()前后写的代码,没有开启线程,肯定对接口响应有影响

2、只能单表操作

第1点可以使用多线程,前后分别开线程,ThreadLocal 配合CountDownLatch在调用保存日志方法,减少对接口响应的影响。

第2点那几乎就是无解了。除非将一个方法里的多表操作拆分成多个方法,一个方法里只操作一张表,如果操作多表就一个方法里调用多个方法,不知道这样是否可行,没有实际试验。

 

你可能感兴趣的:(框架搭建,springboot)