最近刚换了份工作,这家公司主要做的是b端的产品,b端产品一般审计都比较严,所以免不了会有操作日志这个东西,但是我发现现在这家公司的操作日志都是这样收集的,看以下代码:
//记录操作日志 主子表分别添加
List<ContainerLogVO> containerLogVOS = BeanUtil.copyList(ContainerLogVO.class, listExecuteResult.getResult());
containerLogVOS.forEach(e -> {
e.setId(null);
});
ExecuteResult<List<ContainerLogVO>> listExecuteResult1 = containerLogService.addBatch(containerLogVOS);
if (!Constants.SUCCESS_CODE.equals(listExecuteResult1.getCode())) {
throw new Exception("容器日志保存异常");
}
//记录更新日志
GoodsBaseLogVO logVO = BeanUtil.copyPropertes(GoodsBaseLogVO.class, goodsBaseResult.getResult());
logVO.setId(null);
ExecuteResult<GoodsBaseLogVO> logVOExecuteResult = goodsBaseLogService.add(logVO);
if(!Constants.SUCCESS_CODE.equals(logVOExecuteResult.getCode())){
throw new Exception(logVOExecuteResult.getResultMessage());
}
随手粘了两处代码,这么写可不可以? 可以,但是太不面向对象了,O(∩_∩)O哈哈~
于是我就想着给他改造改造。
AOP的五大通知
先说aop的五个通知类型:
- 前置通知 在目标方法执行前执行
- 环绕通知 在目标方法执行之前和之后各执行一遍
- 后置通知 在目标方法执行之后执行
- 异常通知 在目标方法抛出异常后执行
- 最终通知 在目标方法执行之后执行 目标方法无论是异常还是正常都会执行
如果方法出现异常,我这边是不需要记录操作日志的,所以我这边只选择后置通知就可以了(如果有特殊需求可以根据需要进行选择,也可以选择多个)
本次分享 只是给大家分享一个思路。
数据建模
这个表属于方法与日志生成的映射关系表 在给大家看看对应的配置
首先 methodName 我们根据aop是完全可以取到对应的方法名称和映射的mapping,methodInfo这个字段就是配置对应的日志 每个?就相当于一个占位符与后面的paramArr对应,channel和paramName一起说 拿id为1的举例 就相当于是取入参中的vo。大概配置表就是这么一个概念。
在看日志表:
在看看日志表里面存储的信息
具体实现
直接看代码
/**
* @author Jacky
* @create 2022-07-20-14:31
*/
@Component
@Aspect
public class UserLogAspect {
private static final Logger log = LoggerFactory.getLogger(UserLogAspect.class);
@Autowired
private UserOperationLogExportService userOperationLogExportService;
@Resource
private UserColumnRelationDAO userColumnRelationDAO;
@Autowired
private MethodToLogExportService methodToLogExportService;
@Pointcut("execution(* com.niu.wms.api..*.*(..))")
public void allLog(){
}
@AfterReturning(value = "allLog()",returning = "ajaxInfo")
public void saveLog(JoinPoint joinPoint, AjaxInfo ajaxInfo){
// 拼接出对应的方法名以及映射的mapping
// 从切面织入点处通过反射机制获取织入点处的方法
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
// 获取切入点所在的方法
Method method = signature.getMethod();
String methodName = method.getName();
String allMapping = joinPoint.getTarget().getClass().getName().replace("Api", "").replace("Controller", "");
String mapping = allMapping.substring(allMapping.lastIndexOf(".") + 1);
// 根据对应的映射查询配置表 如配置表中无相关的配置则不记录
MethodToLogVO methodToLogVO = methodToLogExportService.queryOneData(MethodToLogVO.builder().methodName(mapping + "/" + methodName).build()).getResult();
if (Utility.isEmpty(methodToLogVO)){
return;
}
String paramName = methodToLogVO.getParamName();
JSONObject json = null;
// 获取对应的参数 把对应的参数转成json格式
if (methodToLogVO.getChannel().equals(0)){
//参数名数组
String[] parameters = signature.getParameterNames();
//参数值
Object[] args = joinPoint.getArgs();
int voIndex = ArrayUtils.indexOf(parameters, paramName);
if (voIndex==-1){
return;
}
Object arg = args[voIndex];
json = JSON.parseObject(JSON.toJSONString(arg));
}else if (methodToLogVO.getChannel().equals(1)){
Object data = ajaxInfo.getData();
json = JSON.parseObject(JSON.toJSONString(data));
}
if (Utility.isEmpty(json)){
return;
}
// 获取到参数之后进行占位符拼接赋值
String paramArr = methodToLogVO.getParamArr();
String[] split = paramArr.split(",");
String methodInfo = methodToLogVO.getMethodInfo();
String context = methodInfo;
for (String s : split) {
context = context.replaceFirst("\\?", json.getString(s));
}
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String userId = request.getHeader("userId");
Map map = userColumnRelationDAO.queryUserInfo(Long.valueOf(userId));
String realIP = IpUtil.getRealIP(request);
String userType1 = request.getHeader("userType");
userType1 = userType1 == null ? "1": userType1;
Integer userType = Integer.parseInt(userType1);
String requestParam = JSON.toJSONString(joinPoint.getArgs());
String response = JSON.toJSONString(ajaxInfo);
StringBuilder result = new StringBuilder();
if (ajaxInfo.getCode()!=0){
result.append("失败:");
result.append(ajaxInfo.getMsg());
}else{
result.append("成功");
}
ExecuteResult<UserOperationLogVO> save = userOperationLogExportService.add(UserOperationLogVO.builder()
.userNumber(map.getOrDefault("userNumber",userId).toString())
.userName(map.getOrDefault("userName",userId).toString())
.logType(userType)
.hostAddress(realIP)
.operationContent(context)
.request(requestParam)
.response(response)
.operationTime(new Date())
.result(result.toString())
.build());
if (!save.getCode().equals(Constants.SUCCESS_CODE)){
log.error("日志保存失败");
log.error("错误信息:"+save.getResultMessage());
}
}
}
大概就是这么个东西,主要是分享一个思路,也有很多可以优化的地方,大家可以根据系统情况自行优化。