设置注解配置类
注解类代码如下:
import com.pub.enums.LogOperationTypeEnum;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
*/
@Documented
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface LogAnnotation {
/**
* 菜单,多级用'/'分隔,最多三级
*/
String classMenu();
/**
* 功能名称
*
* @return
*/
String funcName();
/**
* 操作类型
*
* @see LogOperationTypeEnum
*/
LogOperationTypeEnum operType();
}
增加AOP切面类
Aspect切面类代码如下:
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.pub.config.mvc.AuthContext;
import com.pub.enums.LogOperationTypeEnum;
import com.pub.param.base.CommonResponse;
import com.pub.param.req.IdStatusParam;
import com.pub.param.resp.SysLogBO;
import com.pub.param.resp.UserEditVo;
import com.pub.param.resp.UserVo;
import com.pub.service.impl.RedisService;
import com.pub.service.interfacetask.SysLogService;
import com.pub.util.DateUtil;
import com.pub.util.IpUtil;
import com.pub.util.JsonUtils;
import com.pub.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.annotation.Annotation;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
/**
*
*/
@Slf4j
@Aspect
@Component
public class LogAopAspect {
@Autowired
private SysLogService sysLogService;
@Autowired
private RedisService redisService;
@Pointcut(value = "@annotation(com.pub.aop.LogAnnotation)")
public void logPointCut() {
}
@AfterReturning(value = "logPointCut()&& @annotation(logAnnotation)", returning = "result")
public void afterReturn(JoinPoint joinPoint, LogAnnotation logAnnotation, Object result) {
log.info("AfterReturning 开始记录日志.");
SysLogBO sysOperLogBo = new SysLogBO();
CommonResponse responseVo = getResponseVo(result);
sysOperLogBo.setOperationDetail(responseVo.getCode() + "-" + responseVo.getMessage());
processLog(joinPoint, sysOperLogBo, logAnnotation);
}
/**
* 判断返回对象类型并处理,此处根据项目统一封装返回对象Bean进行调整即可
*/
private CommonResponse getResponseVo(Object result) {
CommonResponse responseVo = new CommonResponse<>();
if (result instanceof CommonResponse) {
responseVo = (CommonResponse) result;
} else if (result instanceof ResponseEntity) {
ResponseEntity responseEntity = (ResponseEntity) result;
int code = responseEntity.getStatusCode().is2xxSuccessful() ? 0 : responseEntity.getStatusCode().value();
responseVo.setCode(code + "");
if (!responseEntity.getStatusCode().is2xxSuccessful()) {
String body = JSON.toJSONString(responseEntity.getBody());
//长度限制,以免超长记录入库失败
responseVo.setMessage(body.length() >= 200 ? body.substring(0, 200) : body);
}
responseVo.setResult(responseEntity.getBody());
} else {
log.warn("aop getResponseVo result: {}", result);
}
return responseVo;
}
/**
* 操作失败,记录异常处理
*
* @param joinPoint
* @param logAnnotation
* @param e
*/
@AfterThrowing(value = "logPointCut() && @annotation(logAnnotation)", throwing = "e")
public void doAfterThrowing(JoinPoint joinPoint, LogAnnotation logAnnotation, Throwable e) {
log.info("doAfterThrowing 开始记录日志.");
SysLogBO sysOperLogBo = new SysLogBO();
String comments = e.getMessage();
if (StringUtils.isNotBlank(comments) && comments.length() > 256) {
comments = comments.substring(0, 255);
}
sysOperLogBo.setOperationDetail(comments);
processLog(joinPoint, sysOperLogBo, logAnnotation);
}
/**
* 日志记录入库
*
* @param joinPoint
* @param sysOperLogBo
* @param logAnnotation
*/
private void processLog(JoinPoint joinPoint, SysLogBO sysOperLogBo, LogAnnotation logAnnotation) {
try {
//记录用户信息
UserVo sysUserDO = AuthContext.getUser();
sysOperLogBo.setUserName(sysUserDO.getLoginNo());
// 记录菜单
String[] menus = logAnnotation.classMenu().split("/");
if (menus.length >= 1) {
sysOperLogBo.setFirstClassMenu(menus[0]);
}
if (menus.length >= 2) {
sysOperLogBo.setSecondClassMenu(menus[1]);
}
if (menus.length >= 3) {
sysOperLogBo.setThirdClassMenu(menus[2]);
}
// sysOperLogBo.setFuncName(logAnnotation.funcName());
// 解析请求参数
Object[] args = joinPoint.getArgs();
HttpServletRequest httpServletRequest = null;
for (Object object : args) {
if (object instanceof HttpServletRequest) {
httpServletRequest = (HttpServletRequest) object;
}
}
if (null == httpServletRequest) {
httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
String requestURI = httpServletRequest.getRequestURI();
if (requestURI.indexOf("hdp") > -1) {
requestURI = requestURI.substring(requestURI.indexOf("hdp") + 3);
}
sysOperLogBo.setVisitUrl(requestURI);
sysOperLogBo.setVisitIp(IpUtil.getRequestIp());
//
String sessionId = System.currentTimeMillis() + StringUtil.getUuid();
HttpSession session = httpServletRequest.getSession();
if (session != null) {
sessionId = session.getId();
}
sysOperLogBo.setSessionId(sessionId);
sysOperLogBo.setOperationTime(DateUtil.getYYYYMMDDHHMMSS(new Date()));
sysOperLogBo.setOperationType(logAnnotation.operType().getDesc());
// 获取请求方式, 获取请求内容类型,请求参数
String requestMethod = httpServletRequest.getMethod();
String contentType = httpServletRequest.getContentType();
Object reqParamJson = getReqParamJson(joinPoint, httpServletRequest, requestMethod, contentType);
log.info("reqParamJson: {}", reqParamJson);
dealParamsLog(sysOperLogBo, reqParamJson, logAnnotation);
// 持久化
log.info(JSONObject.toJSONString(sysOperLogBo));
sysLogService.saveSysLog(sysOperLogBo);
} catch (Exception e) {
log.error("aop deal insert log error: {}", e);
}
}
/**
* 根据参数处理业务日志类型
*
* @param sysOperLogBo
* @param reqParamJson
* @param logAnnotation
*/
private void dealParamsLog(SysLogBO sysOperLogBo, Object reqParamJson, LogAnnotation logAnnotation) {
if (Objects.isNull(reqParamJson)) {
return;
}
// 用户启停
if (logAnnotation.operType() == LogOperationTypeEnum.LOG_USER_DISABLE) {
IdStatusParam statusParam = JsonUtils.toObject(JsonUtils.toJson(reqParamJson), IdStatusParam.class);
// 用户帐号状态, 1正常 0加锁-未授权
if (1 == statusParam.getStatus()) {
sysOperLogBo.setOperationType(LogOperationTypeEnum.LOG_USER_ENABLE.getDesc());
}
}
// 用户启用新增-编辑
else if (logAnnotation.operType() == LogOperationTypeEnum.LOG_USER_EDIT) {
UserEditVo editVo = JsonUtils.toObject(JsonUtils.toJson(reqParamJson), UserEditVo.class);
// 新增/编辑标识,1编辑,0新增
if (0 == editVo.getEdit()) {
sysOperLogBo.setOperationType(LogOperationTypeEnum.LOG_USER_ENABLE.getDesc());
}
sysOperLogBo.setRemarks(editVo.getRemarks());
}
}
/**
* 获取请求参数JSON字符串
*
* @param joinPoint
* @param request
* @param requestMethod
* @param contentType
*/
private Object getReqParamJson(JoinPoint joinPoint, HttpServletRequest request, String requestMethod, String contentType) {
// 判断控制器方法参数中,是否有RequestBody注解
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Annotation[][] annotations = signature.getMethod().getParameterAnnotations();
boolean isRequestBody = false;
for (Annotation[] annotationArray : annotations) {
for (Annotation annotation : annotationArray) {
if (annotation instanceof RequestBody) {
isRequestBody = true;
break;
}
}
}
/**
* 判断请求内容类型
* 通常有3中请求内容类型
* 1.发送get请求时,contentType为null
* 2.发送post请求时,contentType为application/x-www-form-urlencoded
* 3.发送post json请求,contentType为application/json
* 4.发送post json请求并有RequestBody注解,contentType为application/json
*/
Object paramObject = null;
int requestType = 0;
if ("GET".equals(requestMethod)) {
requestType = 1;
} else if ("POST".equals(requestMethod)) {
if (contentType == null) {
requestType = 5;
} else if (contentType.startsWith("application/x-www-form-urlencoded")) {
requestType = 2;
} else if (contentType.startsWith("application/json")) {
if (isRequestBody) {
requestType = 4;
} else {
requestType = 3;
}
}
}
// 1,2,3中类型时, 获取getParameterMap中所有的值, 处理后序列化成JSON字符串
if (requestType == 1 || requestType == 2 || requestType == 3 || requestType == 5) {
Map<String, String[]> paramsMap = request.getParameterMap();
paramObject = paramsMap;
} else if (requestType == 4) { // POST,application/json,RequestBody的类型,简单判断,然后序列化成JSON字符串
Object[] args = joinPoint.getArgs();
if (args == null) {
paramObject = null;
} else if (args.length == 1) {
paramObject = args[0];
} else {
paramObject = args;
// paramObject = args[0];
}
}
return paramObject;
}
}
其余代码举例
代码示例:
package com.pub.enums;
/**
* 日志记录。 操作类型:1、系统登录,2、系统登出
*/
public enum LogOperationTypeEnum {
LOG_TYPE_1(1, "系统登录"),
LOG_TYPE_2(2, "系统登出"),
// 新增账号 = 启用账号
LOG_USER_ENABLE(12, "新增账号"),
LOG_USER_DISABLE(13, "停用账号"),
LOG_USER_EDIT(14, "编辑账号"),
LOG_USER_BATCH_EDIT(15, "批量授权"),
;
private final int code;
private final String desc;
LogOperationTypeEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
public static LogOperationTypeEnum getEnum(int code) {
for (LogOperationTypeEnum logType : values()) {
if (logType.getCode() == code) {
return logType;
}
}
return null;
}
}
使用示例:
/**
* @param userVo
* @return
*/
@LogAnnotation(classMenu = "系统管理/用户管理", funcName = "用户编辑", operType = LogOperationTypeEnum.LOG_USER_EDIT)
@PostMapping("/editUser")
@Operation(summary = "编辑用户信息,修改角色")
public CommonResponse<Boolean> editUser(@RequestBody @Valid UserEditVo userVo) {
}
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* IdStatusParam
*/
@Data
@Schema(title = "主键状态参数")
public class IdStatusParam implements Serializable {
private static final long serialVersionUID = 8159404372669135796L;
@NotNull(message = "id不能为空")
@Schema(title = "主键ID")
private Integer id;
// 若是:用户帐号状态, 1正常 0加锁-未授权
@Schema(title = "状态")
@NotNull(message = "状态不能为空")
private Integer status;
}