数据业务级日志记录业务操作前后数据(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<Object> 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<Integer> addResult = (Result<Integer>) 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点那几乎就是无解了。除非将一个方法里的多表操作拆分成多个方法,一个方法里只操作一张表,如果操作多表就一个方法里调用多个方法,不知道这样是否可行,没有实际试验。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)