最近在学习搭建框架,在这里分享下一些技术搭建,当做自己笔记,不好勿喷。
直接进入主题,对于系统的一些敏感操作,如修改、删除用户等需要增加日志模块,记录用户的操作。但是我们不可能每个模块都写原生代码,构建对象保存到数据库,所以这里就开始用到了aop日志切面,下面是我配置的简单例子。
- 日志类
import cn.ffcs.base.MyBaseModel;
import com.baomidou.mybatisplus.annotation.IdType;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import com.baomidou.mybatisplus.annotation.*;
/**
* <p>
* 用户操作日志
* </p>
*
* @author 86188
* @since 2021-04-16
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("t_sm_userlog")
public class TSmUserlog extends MyBaseModel implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "ID", type = IdType.AUTO)
private Long id;
/**
* 操作人id
*/
@TableField("OPERATE_USER_ID")
private Long operateUserId;
/**
* 操作人
*/
@TableField("OPERATE_USER_NAME")
private String operateUserName;
/**
* 操作模块
*/
@TableField("OPERATE_MODULE")
private String operateModule;
/**
* 操作类
*/
@TableField("OPERATE_CLASS")
private String operateClass;
/**
* 操作方法
*/
@TableField("OPERATE_METHOD")
private String operateMethod;
/**
* 操作类型
*/
@TableField("OPERATE_NAME")
private String operateName;
/**
* 操作内容
*/
@TableField("OPERATE_CONTENT")
private String operateContent;
/**
* 操作时间
*/
@TableField("OPERATE_TIME")
private Date operateTime;
/**
* 请求数据
*/
@TableField("REQUEST_DATA")
private String requestData;
/**
* 响应数据
*/
@TableField("RESPONSE_DATA")
private String responseData;
/**
* 响应code
*/
@TableField("RESPONSE_CODE")
private String responseCode;
/**
* 响应信息
*/
@TableField("RESPONSE_MESSAGE")
private String responseMessage;
/**
* 删除状态
*/
@TableField(value="STATUS", fill = FieldFill.INSERT)
@TableLogic
private String status;
}
- 响应类
/**
* 访问结果Response
*/
@Data
public class Result<T> {
// http 状态码
private int code;
// 返回信息
private String msg;
// 返回的数据
private T data;
//返回失败或者成功
private boolean success;
}
- 定义操作模块
/**
* 定义模块名称枚举
*
* @author 天真热
* @create 2021-04-15 9:43
* @desc
**/
public enum OperateModule {
T_LOGIN("登录"),
T_SM_USER("用户管理"),
T_SM_ROLE("角色管理"),
T_SM_AUTHORITY("权限管理");
private String moudoleName;
private OperateModule(String moudoleName) {
this.moudoleName = moudoleName;
}
/**
* 返回模块名称
*
* @return
*/
public String getMoudoleName() {
return moudoleName;
}
}
- 定义操作行为
/**
* 定义操作类型枚举
*
* @author 天真热
* @create 2021-04-15 9:44
* @desc
**/
public enum OperateType {
LOGIN("登录"),
CREATE("新增"),
MODIFY("修改"),
DELETE("删除"),
SHOW("查看");
private String typeName;
private OperateType(String typeName) {
this.typeName = typeName;
}
/**
* 返回模块名称
*
* @return
*/
public String getTypeName() {
return typeName;
}
}
- 自定义日志注解
/**
* 自定义注解@UserActionLog
*
* @author 天真热
* @create 2021-04-15 9:42
* @desc
**/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface UserActionLog {
/**
* 操作描述 业务名称business
*
* @return
*/
String description() default "";
/**
* 操作模块
*
* @return
*/
OperateModule module();
/**
* 操作类型 create modify delete
*
* @return
*/
OperateType opType();
}
- 自定义日志操作
/**
* 切面
*
* @author 天真热
* @create 2021-04-15 9:45
* @desc
**/
@Aspect
@Component
public class WebLogAspect {
@Resource
private ITSmUserlogService userlogService;
@Resource
private ITSmUserService userService;
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 获取@UserActionLog 注解上信息
*
* @param joinPoint
* @return map
* @throws Exception
*/
public static Map<String, Object> getControllerAnnotationValue(JoinPoint joinPoint) throws Exception {
Map<String, Object> map = new HashMap<>();
//获取切入点类名
String targetName = joinPoint.getTarget().getClass().getName();
map.put("targetName", targetName);
//获取切入点方法名
String methodName = joinPoint.getSignature().getName();
map.put("methodName", methodName);
//获取切入点参数列表
Object[] arguments = joinPoint.getArgs();
//获取切入点类对象
Class targetClass = Class.forName(targetName);
//获取切入点类对象的所有方法
Method[] methods = targetClass.getMethods();
//遍历类方法
for (Method method : methods) {
if (method.getName().equals(methodName)) {
//返回Class对象数组
Class[] classes = method.getParameterTypes();
if (classes.length == arguments.length) {
//操作描述
String description = method.getAnnotation(UserActionLog.class).description();
//模块名
String module = method.getAnnotation(UserActionLog.class).module().name();
//操作类型
String opType = method.getAnnotation(UserActionLog.class).opType().name();
map.put("module", module);
map.put("opType", opType);
map.put("business", description);
break;
}
}
}
return map;
}
/**
* 定义一个切入点.
* 第一个 * 代表任意修饰符及任意返回值.
* 第二个 * 任意包名
* 第三个 * 定义在web包或者子包
* 第四个 * 任意方法
* .. 匹配任意数量的参数.
*/
@Pointcut("execution(public * cn.tzr..*.controller..*.*(..)) && @annotation(cn.ffcs.config.aop.userlog.UserActionLog)")
public void webLog() {
}
@Around("webLog()")
public Object round(ProceedingJoinPoint joinPoint) throws Throwable {
// logger.info("环绕通知开始........");
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//获取token
String shortToken = request.getHeader(Constant.SHORT_TOKEN);
if (CommonStringUtils.isEmpty(shortToken)) {
//前后端不分离的情况下,需要考虑地址栏带token的情况
if (Constant.IS_URL_TOKEN) {
shortToken = request.getParameter(Constant.SHORT_TOKEN);
}
}
//解析token获取用户
String username = JWTUtil.getUsername(shortToken);
if(username==null){
//没登录
return null;
}
TSmUserDTO userDTO = userService.getByUsername(username);
String nickname = userDTO.getNickname();
//获取切入点的参数列表
Object[] args = joinPoint.getArgs();
//获取切入点的入参名称列表
String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
Map<String, Object> params = new HashMap<>();
//获取所有参数对象
for (int i = 0; i < args.length; i++) {
if (null != args[i]) {
if (args[i] instanceof BindingResult) {
params.put(paramNames[i], "bindingResult");
} else {
params.put(paramNames[i], args[i]);
}
} else {
params.put(paramNames[i], "无");
}
}
//获取@controllerLog 注解上信息
Map<String, Object> values = getControllerAnnotationValue(joinPoint);
String opType = values.get("opType").toString();//操作类型
String module = values.get("module").toString();//操作模块
String business = values.get("business").toString();//业务
//切面获取返回值
Object returnValue = joinPoint.proceed();
Result result = new Result();
if (returnValue instanceof Result) {
result = (Result) returnValue;
}
TSmUserlogDTO userlogDTO = new TSmUserlogDTO();
userlogDTO.setOperateModule(OperateModule.valueOf(module).getMoudoleName());//操作模块
userlogDTO.setOperateName(OperateType.valueOf(opType).getTypeName());//操作类型
userlogDTO.setOperateContent(business);//操作内容
userlogDTO.setRequestData(JSONObject.fromObject(params).toString());//数据
userlogDTO.setOperateUserId(userDTO.getId());//操作人id
userlogDTO.setOperateUserName(userDTO.getNickname());//操作人名称
userlogDTO.setOperateTime(new Date());//操作时间
userlogDTO.setResponseCode(CommonStringUtils.toString(result.getCode()));//响应码
userlogDTO.setResponseMessage(result.getMsg());//响应信息
String responseData = null;
try {
responseData = CommonStringUtils.toString(JSONObject.fromObject(result.getData()));//响应数据
} catch (Exception e) {
responseData = CommonStringUtils.toString(result.getData());//响应数据
}
if (responseData.length() > 450) {
responseData = responseData.substring(0, 450)+"......";
}
userlogDTO.setResponseData(responseData);//响应数据
userlogDTO.setOperateClass(CommonStringUtils.toString(values.get("targetName")));
userlogDTO.setOperateMethod(CommonStringUtils.toString(values.get("methodName")));
userlogService.save(userlogDTO);
return returnValue;
}
@AfterReturning("webLog()")
public void doAfterReturning(JoinPoint joinPoint) {
// 处理完请求,返回内容
logger.info("WebLogAspect.doAfterReturning()");
}
}
- 需要记录日志的地方加上 @UserActionLog即可,如下
/**
* 保存用户
*
* @return
*/
@PostMapping("/user/save")
@UserActionLog(description = "保存用户", module = OperateModule.T_SM_USER, opType = OperateType.CREATE)
public Result save(@RequestBody JSONObject data) {
return null;
}
基本能直接套用、亲测有效!!!