当前位置: 首页 > news >正文

写了一个关于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 工具类,时间工具类等获取的方式可以在很多的地方有!或者自己可以写

相关文章:

  • 从入门到精通汇编语言 第七章(高级汇编语言技术)
  • goweb项目结构以及如何实现前后端交互
  • 【GoChat】密码处理与实现JWT+进行功能单测
  • MuJoCo 关节角速度记录与可视化,监控机械臂运动状态
  • 【特殊场景应对9】视频简历的适用场景与风险分析
  • 测试模板x
  • AI 与高性能计算的深度融合:开启科技新纪元
  • 【互联网架构解析】从物理层到应用层的全栈组成
  • 前端开发本地配置 HTTPS 全面详细教程
  • Java 自定义TCP协议:【特点编码字符串<=>字节<=>特点编码16进制】16进制字符串和编码的转换 (各种编码通过字节向16进制的互转)| XOR计算
  • 在华为云平台上使用 MQTT 协议:构建高效可靠的物联网通信
  • crossOriginLoading使用说明
  • gradle-缓存、依赖、初始化脚本、仓库配置目录详解
  • 【锂电池剩余寿命预测】GRU门控循环单元锂电池剩余寿命预测(Matlab完整源码)
  • 发布-订阅模式应用场景及示例说明
  • 桥接模式(Bridge Pattern)详解
  • Eigen矩阵操作类 (Map, Block, 视图类)
  • 系统设计(2)—Redis—消息队列—数据库—熔限降
  • babel核心知识点
  • 理想星环OS选择NuttX作为MCU侧OS的核心原因分析​
  • “富卫保险冠军赛马日”创双纪录,打造赛马旅游盛宴,印证香港联通国际优势
  • 13家券商一季报出炉:超七成业绩预喜,财通、湘财、第一创业下滑
  • 2025年“畅游江淮 合肥等侬”文旅推介会在沪成功举办
  • 价格周报|猪价继续回暖:二次育肥热度仍存,对猪价仍有一定支撑
  • 中青报:“猿辅导员工猝死”事件上热搜,是对健康职场环境的共同关切
  • 四川:全省统一取消普通住宅和非普通住宅标准