前提
前段时间客户提出一个需求,想知道谁进了系统干了什么事,但又不是什么都需要记录。所以就想写个注解与切面来实现这个功能。代码粗糙,有什么不对的地方望大神指正。
实现效果
注解放于controller的接口,填上事件名。因为我想记录操作对象的主键是什么,所以还有个id,这个id填的是我传递对象的主键名称。具体做法看下面代码。
在用户界面访问接口后,数据库中将记录操作人,事件名。传递的参数是什么。以及创建时间。有这个数据在,前端就可以按需进行展示了。
代码
- 注解类
/**
* Description: 简单记录操作日志
* @return 返回类型
* @author 格兰德法则·祝
* Create time 2021/5/24 10:26
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OperationLog {
// 可以直接填入人员。这个项目中人员是通过前端传递的token进行解析获取的人员
String name() default "未知人员";
// 事件名称,根据接口用途自行填入。
String event() default "未知事件";
// 对象主键名称,要根据传递对象的主键来进行修改。若是传的是个String。那么直接从参数(数据库)中获取。
String id() default "未知主键";
}
2.切面类
/**
* Description: 日志切面
* @return 返回类型
* @author 格兰德法则·祝
* Create time 2021/5/24 10:26
*/
@Component
@Aspect
@Slf4j
public class OperationLogAspect {
@Resource
private UserOrgManageFeign userOrgManageFeign;
@Resource
private LogInfoMapper logInfoMapper;
@Pointcut("@annotation(com.dlg.patrol.annotation.OperationLog)")
private void pointCut(){}
@Before("pointCut()&& @annotation(operationLog)")
public void before(OperationLog operationLog){
}
@Around("pointCut() && @annotation(operationLog)")
public Object advice(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws JsonProcessingException {
String realName = "";
String id = "";
String value = "";
Object result = null;
// 获取接口参数
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
if (args[i] == null) {
continue;
}
// 这是注解中的ID,根据注解上填写的id="id名",通过 id名 反射获取传递的对象类中相应ID
if (!"未知主键".equals(operationLog.id()) && !(args[i] instanceof HttpServletRequest)) {
// ↓反射获取主键。这里比较粗糙。
// 主键一般在继承的父类中,并且是私有的。
Field[] fields = args[i].getClass().getFields();
Field[] declaredFields = args[i].getClass().getSuperclass().getDeclaredFields();
try {
for (Field field : fields) {
field.setAccessible(true);
if (operationLog.id().equals(field.getName())) {
id = (String)field.get(args[i]);
}
}
for (Field field : declaredFields) {
field.setAccessible(true);
if (operationLog.id().equals(field.getName())) {
id = (String)field.get(args[i]);
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
// 这是获取操作人的。我们项目中是通过HttpServletRequest中的header中的token来获取人员信息的。这里可以根据自身项目情况进行修改
if(args[i] instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest)args[i];
String userId = RedisUtil.getUserId(request);
realName = userOrgManageFeign.getUserInfo(userId).getData().getRealName();
break;
}else {
ObjectMapper mapper = new ObjectMapper();
value = value + " parameter: " + mapper.writeValueAsString(args[i]);
}
}
// 这里就是存进操作日志表的操作
try {
LogInfo logInfo = new LogInfo();
logInfo.setCreatedOn(new Date());
logInfo.setEvent(operationLog.event());
logInfo.setLogId(UUID.randomUUID().toString());
if(!"".equals(id)){
logInfo.setEventId(id);
}
logInfo.setParameter(value);
logInfo.setUserName(realName);
logInfoMapper.insertSelective(logInfo);
} catch (Exception e) {
e.printStackTrace();
}
try {
result = joinPoint.proceed(args);
} catch (Throwable throwable) {
throwable.printStackTrace();
}
// 这里是返回的信息了,不做处理直接返回,结合自身项目进行修改
if(result instanceof ApiResult) {
ApiResult apiResult = (ApiResult) result;
return apiResult;
}
return result;
}
@After("pointCut()&& @annotation(operationLog)")
public void after(OperationLog operationLog){
}
}
3.使用
注解的使用上面有就不在描述了。
总结
水平有限,写的比较简单,有什么不对的地方希望大神指点。谢谢。