写了一个关于SpringAop记录用户操作的功能
1:先思考自己的需要记录什么功能,创建对应的数据库表。
各位开发人员如果在公司一定要写关于建表注释,方便同事观看,对于自己来说是最基础的代码素养,对于自己的职业来说这是职业最基本的规范。当然如果是那样的,对吧,懂的都懂。不懂就以后就会的懂了。
代码仅供参考,实际请根据自身工作内容来做。
-- 建表语句
CREATE TABLE sys_oper_log (id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID,自增',module VARCHAR(50) COMMENT '系统模块(如订单模块,商品管理,员工及达人等)',operation_type VARCHAR(20) COMMENT '操作类型(生成删除、修改,新增,查询等)',operator VARCHAR(50) COMMENT '操作人员(如admin)',operator_id VARCHAR(32) COMMENT '操作人员ID',ip VARCHAR(50) COMMENT '操作IP',location VARCHAR(100) COMMENT '操作地点(如广东省广州市)',status VARCHAR(20) COMMENT '操作状态(成功/失败)',operation_time DATETIME COMMENT '操作时间',api_interface VARCHAR(200) COMMENT '操作接口(如/api/xxx/xxxxx)',duration BIGINT COMMENT '消耗时间(毫秒)',remark VARCHAR(200) COMMENT '备注(如中继)',create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间(记录插入时的时间)'
)COMMENT='操作系统记录表:记录系统中用户对模块的操作信息(如操作类型、操作人员、操作时间等)';-- 索引
CREATE INDEX idx_operator ON sys_oper_log(operator);
CREATE INDEX idx_status ON sys_oper_log(status);
CREATE INDEX idx_operation_time ON sys_oper_log(operation_time);
2:创建自定义注解,自定义注解在很多的时候都会有用,例如日志审计,权限控制,数据校验,API文档生产,事务与缓存控制,代码生成与测试等等。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface SysLogModuleAnno {// 定义接口描述,默认为空String descrption() default "";
}
3:因为我这次写的是一个模块监控,监控当前登录人进行了哪些操作。
@Aspect
@Component
public class SysLogModuleAop {// 日志记录器private static final Logger log= LoggerFactory.getLogger(SysLogModuleAop.class);// 日志服务@Resourceprivate sysLogService sysLogService;/*** 定义切点:拦截所有带有@SysLogModuleAnno注解的方法*/@Pointcut("@annotation(com.lion.mall.base.annotation.SysLogModuleAnno)")public void sysLogModulePointCut() {//pointcut}/*** 环绕通知:在目标方法执行前后进行日志记录* @param point 连接点* @return 目标方法执行结果* @throws Throwable 可能抛出的异常*/@Around("sysLogModulePointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {// 1. 获取日志请求信息SysLogModuleSaveRequest sysLogModuleRequest = this.getSysLogModuleSaveRequest(point);Object result = null;try {// 2. 执行目标方法result = point.proceed();// 3. 如果没有异常,设置操作状态为成功 (0)sysLogModuleRequest.setOperStatus("0"); // 成功}catch (Throwable e){// 4. 如果有异常,设置操作状态为失败 (1)sysLogModuleRequest.setOperStatus("1"); // 失败sysLogModuleRequest.setRemark(e.toString()); // 记录异常信息throw e; // 继续抛出异常,不影响原有逻辑}finally {// 5. 解析结果并设置操作结果(即使发生异常也可以记录部分信息)if (result != null) {Map parseObject = JSONObject.parseObject(JSON.toJSONString(result), Map.class);sysLogModuleRequest.setOperResult(MapUtils.getString(parseObject, "resultMessage"));}// 6. 异步保存日志new SysLogModuleAddThread(sysLogService, sysLogModuleRequest).start();}return result;}/*** 异常通知:当目标方法抛出异常时记录日志* @param point 连接点* @param e 抛出的异常*/@AfterThrowing(pointcut="sysLogModulePointCut()",throwing="e")public void doAfterThrowing(JoinPoint point, Throwable e) {// 1. 获取日志请求信息SysLogModuleSaveRequest sysLogModuleRequest = this.getSysLogModuleSaveRequest(point);// 2. 设置异常信息sysLogModuleRequest.setRemark(e.toString());// 3. 异步保存日志new SysLogModuleAddThread(sysLogService,sysLogModuleRequest).start();}/*** 构建日志保存请求对象* @param point 连接点* @return 日志保存请求对象*/private SysLogModuleSaveRequest getSysLogModuleSaveRequest(JoinPoint point) {// 1. 获取当前HTTP请求HttpServletRequest httpRequest = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();// 2. 从请求头获取token并解析用户信息String token = httpRequest.getHeader("x-xxxx-token"); //前端命名User user = TokenUtil.recreateUserFromToken(token);// 3. 构建日志请求对象并设置基本信息SysLogModuleSaveRequest logRequest=new SysLogModuleSaveRequest();logRequest.setShopId(user .getShopId());logRequest.setResourceModule("xxxxxxxx");logRequest.setResourceName("XXXX系统");logRequest.setServerIp(httpRequest.getRemoteHost());logRequest.setServerPort(String.valueOf(httpRequest.getRemotePort()));logRequest.setClientIp(IpUtil.getIpAddr(httpRequest));logRequest.setOperTime(DateUtil.getToday());//操作人员工IDlogRequest.setOperUserId(user.getStaffId());//系统操作人员名称logRequest.setOperUser(user.getUserName());//创建时间logRequest.setCreateTime(DateUtil.getToday());// 4. 设置方法相关信息logRequest.setMethodName(getControllerMethodDescription(point));logRequest.setMethodUrl(httpRequest.getRequestURI());logRequest.setMethodParams(getParams(point,httpRequest));return logRequest;}/*** 获取方法参数* @param point 连接点* @param httpRequest HTTP请求对象* @return 参数字符串(JSON格式)*/private String getParams(JoinPoint point,HttpServletRequest httpRequest) {Map<String,Object> paramMap = new HashMap<>();// 1. 获取方法参数Object[] args = point.getArgs();MethodSignature signature=(MethodSignature)point.getSignature();Method method=signature.getMethod();// 2. 获取参数名称ParameterNameDiscoverer pnd =new DefaultParameterNameDiscoverer();String[] parameterNames = pnd.getParameterNames(method);// 3. 过滤特殊类型参数并构建参数Mapfor(int i=0;i<parameterNames.length;i++) {if(args[i] instanceof MultipartFile ||args[i] instanceof ServletRequest ||args[i] instanceof ServletResponse) {continue;// 跳过文件、请求和响应对象}paramMap.put(parameterNames[i],args[i]);}// 4. 添加请求参数Map<String,String[]> parameterMap=httpRequest.getParameterMap();if(ObjectUtils.isEmpty(parameterMap)) {paramMap.putAll(parameterMap);}return JSON.toJSONString(paramMap);}/*** 获取接口描述信息(从@SysLogModuleAnno注解中获取)* @param point 连接点* @return 方法描述*/private String getControllerMethodDescription(JoinPoint point) {String description="";try {// 获取连接点目标类名String targetName=point.getTarget().getClass().getName();// 获取连接点签名的方法名String methodName=point.getSignature().getName();// 获取连接点参数Object[] args=point.getArgs();// 根据连接点类的名字获取指定类Class targetClass=Class.forName(targetName);// 获取类里面的方法Method[] methods=targetClass.getMethods();for(Method method: methods) {if(method.getName().equals(methodName)) {Class[] clazzs=method.getParameterTypes();if(clazzs.length==args.length) {description=method.getAnnotation(SysLogModuleAnno.class).descrption();break;}}}}catch(Exception e) {log.error("SysLogModuleAop.getControllerMethodDescription error:{"+e.getMessage() +"}");}return description;}
4:返回结果集
@Data
public class SysLogModuleSaveRequest {private static final long serialVersionUID = xxxxxxxxxxxxxxxxxxL;private Long shopId;private String resourceModule;private String resourceName;private String serverIp;private String serverPort;private String clientIp;private String methodName;private String methodUrl;private String methodParams;private String operUser;private Date operTime;private String operResult;private String remark;//新增字段 系统操作人员IDprivate String operUserId;//新增字段 系统更新时间private Date createTime;//新增字段 请求0成功或者1失败private String operStatus;//分页参数public Integer page;public Integer size;//时间参数private Timestamp startTime;private Timestamp endTime;//员工IDprivate String staffId;
}
5:关于Token 还有 Ip 工具类,时间工具类等获取的方式可以在很多的地方有!或者自己可以写