保存系统的操作日志,通过swagger注解获取请求描述(通用版本)
Posted 技术武器库
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了保存系统的操作日志,通过swagger注解获取请求描述(通用版本)相关的知识,希望对你有一定的参考价值。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
前言
系统之前没有全局的记录操作日志,只是个别单个功能记录了操作日志,这样个别功能没有记录操作日志,就会出现和运营扯皮的问题,我们4月份就出现这个问题,活动商品在活动结束后,运营统计商品下单数量,发现超卖了,卖的数量比上架的库存要多,找了两天发现是运营在活动进行中的时候,改了商品库存(第一次上架库存的运营和改库存的运营不是一个人,而且改后没有同步),还好最后发现编辑活动的操作日志是有记录的,如果没有呢?那是不是要开发背锅了,之前就想趁早做掉这个功能,一直拖着,通过这件事后,没商量了,直接开干。
操作日志表结构
CREATE TABLE `t_operator_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`create_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
`modify_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改日期',
`operator_id` bigint(20) DEFAULT NULL COMMENT '操作人ID',
`operator` varchar(50) DEFAULT NULL COMMENT '操作人',
`operator_phone` varchar(50) DEFAULT NULL COMMENT '操作人手机号',
`class_path` varchar(255) DEFAULT NULL COMMENT '请求路径前缀',
`method_path` varchar(255) DEFAULT NULL COMMENT '请求路径后缀',
`request_path` varchar(255) DEFAULT NULL COMMENT '请求路径',
`class_name` varchar(255) DEFAULT NULL COMMENT '全限定类名',
`method_name` varchar(50) DEFAULT NULL COMMENT '方法名',
`class_desc` varchar(255) DEFAULT NULL COMMENT '类描述',
`method_desc` varchar(255) DEFAULT NULL COMMENT '方法描述',
`request_parameters` text COMMENT '满参数json',
`request_exists_parameters` text COMMENT '传递参数json',
`result` text COMMENT '返回值',
`system` varchar(255) DEFAULT NULL COMMENT '系统',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='工厂后台操作记录表';
代码
我是通过切面的方式拦截请求,通过获取代理类的swagger注解和mapping注解的方式,获取描述和请求路径,当然还包含请求参数和响应参数。代码中获取操作人的代码,你们结合自己项目改下,其他的通用
package com.zcckj.puxian.web.common.aspect;
import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.ArrayUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.zcckj.puxian.account.api.model.dto.OperatorLogDTO;
import com.zcckj.puxian.account.api.service.write.IOperatorLogWriteService;
import com.zcckj.puxian.web.security.util.CurrentAdminUtil;
import com.zcckj.puxian.web.utils.DingDingUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
/**
* @author lwh
* @date 2022/5/7
* @description 操作日志切面类
**/
@Aspect
@Component
@Slf4j
public class OperationLogAspect
@Value("$spring.application.name")
private String system;
@Reference(version = "1.0.0", group = "$dubbo.service.group", check = false)
private IOperatorLogWriteService operatorLogWriteService;
@Pointcut("execution(public * com.zcckj.puxian.web.controller..*.*(..))")
public void logPointCut()
@Around("logPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable
OperatorLogDTO operatorLog = new OperatorLogDTO();
Class<?> aClass = point.getTarget().getClass();
try
Long currentId = null;
String currentMobile = null;
String currentName = null;
try
currentId = CurrentAdminUtil.getCurrentId();
currentMobile = CurrentAdminUtil.getCurrentMobile();
currentName = CurrentAdminUtil.getCurrentName();
catch (Exception e)
// skip
String name = aClass.getName();
String param1 = this.getParam(point, true);
String param2 = this.getParam(point, false);
// 操作人ID
operatorLog.setOperatorId(currentId)
// 操作人
.setOperator(currentName)
// 操作人手机号
.setOperatorPhone(currentMobile)
// 全限定类名
.setClassName(name)
// 方法名
.setMethodName(point.getSignature().getName())
// 满参数json
.setRequestParameters(param1)
// 传递参数json
.setRequestExistsParameters(param2)
// 系统
.setSystem(system);
// 类描述
this.setClassDesc(operatorLog, aClass);
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
// 请求路径
this.getRequestPath(aClass, method, operatorLog);
// 方法描述
this.setMethodDesc(operatorLog, method);
catch (Exception e)
String exMsg = StrFormatter.format("操作日志切面,执行代理方法前。class:\\n异常Error:\\n", aClass.getName(), ExceptionUtils.getStackTrace(e));
log.error(exMsg);
DingDingUtil.sendDingTalkRisk(exMsg, null, DingDingUtil.DingTalkEnum.BASE_GIVE_AN_ALARM);
//执行方法
Object result = point.proceed();
String resultJson = null;
try
resultJson = this.postHandle(result);
catch (Exception e)
String exMsg = StrFormatter.format("操作日志切面,执行代理方法后,转换返回值为Json,异常Error:", ExceptionUtils.getStackTrace(e));
log.error(exMsg);
DingDingUtil.sendDingTalkRisk(exMsg, null, DingDingUtil.DingTalkEnum.BASE_GIVE_AN_ALARM);
// 返回值
operatorLog.setResult(resultJson);
CompletableFuture.runAsync(() -> operatorLogWriteService.save(operatorLog));
return result;
/**
* 获获取方法描述
*
* @author lwh
* @date 2022/5/7
**/
private void setMethodDesc(OperatorLogDTO operatorLog, Method method)
ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
if (Objects.nonNull(apiOperation) && StringUtils.isNotBlank(apiOperation.value()))
// 方法描述
operatorLog.setMethodDesc(apiOperation.value());
/**
* 取类描述
*
* @author lwh
* @date 2022/5/7
**/
private void setClassDesc(OperatorLogDTO operatorLog, Class<?> aClass)
Api api = aClass.getAnnotation(Api.class);
if (Objects.nonNull(api) && ArrayUtil.isNotEmpty(api.tags()))
// 类描述
operatorLog.setClassDesc(api.tags()[0]);
/**
* 获取返回值
*
* @author lwh
* @date 2022/5/7
**/
private String postHandle(Object retVal)
if (null == retVal)
return "";
return JSONObject.toJSONString(retVal, SerializerFeature.WriteMapNullValue);
/**
* 获取参数名和参数值
*
* @author lwh
* @date 2022/5/7
**/
public String getParam(ProceedingJoinPoint proceedingJoinPoint, boolean isWriteNullValue)
Map<String, Object> map = new LinkedHashMap<>();
Object[] values = proceedingJoinPoint.getArgs();
String[] names = ((CodeSignature) proceedingJoinPoint.getSignature()).getParameterNames();
for (int i = 0; i < names.length; i++)
map.put(names[i], values[i]);
if (values[i] instanceof MultipartFile)
map.put(names[i], null);
if (isWriteNullValue)
return JSONObject.toJSONString(map, SerializerFeature.WriteMapNullValue);
return JSONObject.toJSONString(map);
/**
* 获取请求路径
*
* @author lwh
* @date 2022/5/7
**/
private void getRequestPath(Class<?> aClass, Method method, OperatorLogDTO operatorLog)
RequestMapping classRequestMapping = aClass.getAnnotation(RequestMapping.class);
String methodDesc = "";
GetMapping getMapping = method.getAnnotation(GetMapping.class);
PostMapping postMapping = method.getAnnotation(PostMapping.class);
PutMapping putMapping = method.getAnnotation(PutMapping.class);
DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
if (Objects.nonNull(getMapping) && ArrayUtil.isNotEmpty(getMapping.value()))
methodDesc = getMapping.value()[0];
if (Objects.nonNull(postMapping) && ArrayUtil.isNotEmpty(postMapping.value()))
methodDesc = postMapping.value()[0];
if (Objects.nonNull(putMapping) && ArrayUtil.isNotEmpty(putMapping.value()))
methodDesc = putMapping.value()[0];
if (Objects.nonNull(deleteMapping) && ArrayUtil.isNotEmpty(deleteMapping.value()))
methodDesc = deleteMapping.value()[0];
if (Objects.nonNull(requestMapping) && ArrayUtil.isNotEmpty(requestMapping.value()))
methodDesc = requestMapping.value()[0];
if (Objects.nonNull(classRequestMapping) && ArrayUtil.isNotEmpty(classRequestMapping.value()))
// 请求路径前缀
operatorLog.setClassPath(classRequestMapping.value()[0]);
// 请求路径
operatorLog.setRequestPath(classRequestMapping.value()[0] + methodDesc);
// 请求路径后缀
operatorLog.setMethodPath(methodDesc);
保存数据的效果
白嫖到这,我劝你点赞再走。
以上是关于保存系统的操作日志,通过swagger注解获取请求描述(通用版本)的主要内容,如果未能解决你的问题,请参考以下文章