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

Java项目实现【记录系统操作日志】功能

✨ 哈喽,屏幕前的每一位开发者朋友,你们好呀!✨​

当你点开这篇文章时,或许正对着 IDE 里闪烁的光标发呆,或许刚解决一个卡了三天的 bug,正端着咖啡松口气 —— 不管此刻的你在经历什么,都想先和你说声:“辛苦了,同行者!” 👋​

作为一名摸爬滚打多年的开发工程师,我始终觉得,我们敲出的每一行代码,不仅是业务逻辑的堆叠,更是无数个 “踩坑与爬坑” 的缩影。从第一次上线时的手忙脚乱,到如今能冷静应对突发 bug;从对着文档啃源码的迷茫,到能给新人讲清设计思路 —— 这些藏在键盘敲击声里的成长,太值得被好好梳理和分享了。 📝​

所以,这一系列文章里,不会有太多高深的架构理论,也不会罗列晦涩的技术文档。我想聊的,是那些 “教科书里没写” 的实战细节:​

比如上线前必做的 3 个自查动作(亲测能减少 80% 的线上问题)🛡️;​

比如和产品经理 “友好沟通” 需求的 5 个小技巧(避免反复改需求到崩溃)🤝;​

比如如何用最少的时间,快速定位线上性能瓶颈(曾靠这招拯救过一次紧急故障)🚀;​

再比如那些看似 “浪费时间” 的重构,其实藏着怎样的长期价值…… ♻️​

当然,更想和大家聊聊 “技术之外” 的事:如何平衡加班与生活(毕竟身体是敲代码的本钱)💪,如何在团队中清晰表达自己的想法(别让好方案被沉默埋没)🗣️,甚至是 “35 岁焦虑” 来袭时,我是如何调整心态的…… 🌱​

如果你也和我一样,相信 “经验不是用来炫耀的资本,而是能帮同行少走弯路的路灯”,那不妨坐下来喝杯茶,一起在评论区聊聊:你最近遇到的最大挑战是什么?有没有哪个瞬间,让你觉得 “啊,原来我真的成长了”? 💬​

毕竟,开发这条路从来不是孤军奋战。我们分享的每一个踩坑故事,都可能成为别人的指路牌;你留下的每一条评论,或许也会给我新的启发。 🌟​

那么,准备好了吗?让我们开始这场 “代码背后的成长对话” 吧!接下来的每一篇,都等你来拍砖、补充、共鸣 —— 因为最好的经验,永远在交流里生长。 🌱​

目录

一.需求分析

二.实现逻辑(完全由后端实现)

1.自定义注解OperatorLog

2.在所有controller的方法上,添加该自定义注解

3.编写切面类OperationLogAspect

三.效果展示

1.使用一下管理系统的某个功能

2.查看数据库,肯定会多一条操作日志

四.特别声明


一.需求分析

我要实现的效果是,我访问管理系统的任何一个功能之后,都要往数据库插入一条操作日志。

举例:我在管理系统中,新增了一条合同记录,此时就需要往数据库插入一条操作日志,样式如下

二.实现逻辑(完全由后端实现)

1.自定义注解OperatorLog

@Target(ElementType.METHOD)//表明这个注解只能加在方法上
@Retention(RetentionPolicy.RUNTIME)//表明这个注解在程序运行的时候还能被读取到,而不是用完就丢)
public @interface OperationLog {String moduleName();//模块名String operationType();//操作类型
}

2.在所有controller的方法上,添加该自定义注解

举例:

3.编写切面类OperationLogAspect

@Aspect
@Component
public class OperationLogAspect {//注入SysOperationLogMapper对象@Autowiredprivate SysOperationLogMapper sysOperationLogMapper;//拦截所有加了@OperationLog注解的方法,进行功能无侵入增强(环绕通知)@Around("@annotation(operationLog)")public Object around(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {//ProceedingJoinPoint joinPoint:拦截的方法本身(我们要拦截controller层的方法)//OperationLog operationLog:在所拦截的方法上,添加的注解内容//获取当前的HTTP请求对象HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();//1.获取请求的controller方法的模块名称、操作类型(从注解中获取)String moduleName = operationLog.moduleName();String operationTypeStr = operationLog.operationType();//2.获取本次请求的请求方式(如GET、POST、PUT、DELETE)String requestMethod = request.getMethod();//3.获取请求发起人的用户名称(直接从请求对象中获取即可(别的服务放进去的))//3.1先获取controller方法的参数实体类Object[] args = joinPoint.getArgs();Object entity = args[0];//必须默认controller的实体类参数放在第一个//3.2通过反射,拿到实体类中的用户名称(curUserName)Method getCurUserName = entity.getClass().getMethod("getCurUserName");String operator = (String)getCurUserName.invoke(entity);//4.获取请求发起人的主机ipString hostIp = ClientIpUtil.getClientIpAddress(request);//5.获取操作时间(就是当前的时间)Date operationTime = new Date();//6.先默认操作状态为成功Integer operationStatus = 1;//7.获取请求方法的全类名,如:com.hxb.xltj.controller.SysLogControllerString methodFullName = joinPoint.getTarget().getClass().getName();//System.out.println(methodFullName);//8.被增强的controller方法的返回结果(字符串类型)String methodResult = null;//9.请求路径String requestUrl = request.getRequestURI();//10.请求参数String requestParam = JSON.toJSONString(entity);try{//放行被增强的方法Object result = joinPoint.proceed();//查看被增强方法是否执行成功if(result instanceof ResultBody){ResultBody resultBody = (ResultBody) result;//如果返回的结果响应码不是“000000”,则说明请求失败了,直接令该日志的状态为0(失败)operationStatus = !resultBody.getReturnCode().equals("000000")? 0:1;//如果是“导出”操作,就将返回体ResultBody的data属性设为空(不给前端展示,太长了)if(operationTypeStr.equals("导出")){methodResult = "";}else{//给返回结果赋值methodResult = result.toString();}}//将结果返回出去,不要让切面吞被增强方法的原有逻辑return result;}catch(Exception e){throw e;//继续抛出异常,不影响原有错误处理}finally{//看看都获取到了哪些日志信息//System.out.println(moduleName);//测试模块//System.out.println(operationType);//查询//System.out.println(requestMethod);//POST//System.out.println(hostIp);//127.0.0.1//System.out.println(operationTime);//Thu Aug 21 10:48:07 CST 2025//System.out.println(operationStatus);//1(代表成功)//将操作类型转为整型,再存入数据库Integer operationType = operationTypeStr.equals("新增")?1:operationTypeStr.equals("删除")?2:operationTypeStr.equals("修改")?3:operationTypeStr.equals("查询")?4:operationTypeStr.equals("授权")?5:operationTypeStr.equals("导出")?6:operationTypeStr.equals("导入")?7:operationTypeStr.equals("强退")?8:operationTypeStr.equals("生成代码")?9:operationTypeStr.equals("清空数据")?10:null;//封装系统操作日志记录对象SysOperationLog sysOperationLog = new SysOperationLog(null, moduleName, operationType, requestMethod, operator, hostIp, operationStatus, operationTime,methodFullName,methodResult,requestUrl, requestParam,0,null,null);//将系统操作日志,存入数据库sysOperationLogMapper.insert(sysOperationLog);}}
}

上述代码,核心逻辑就是:获取到【系统日志表】需要的所有字段,然后写入数据库即可。

三.效果展示

1.使用一下管理系统的某个功能

2.查看数据库,肯定会多一条操作日志

四.特别声明

1.如果你想让管理系统的所有行为,都记录到操作日志表中,你需要把后端所有的controller方法,都加上自定义注解@OperatorLog(因为只有这样,才会被AOP切面进行代码无侵入增强)

2.由于AOP切面类OperationLogAspect里面,有一些反射操作(通过controller方法的形参获取操作信息),因此需要团队伙伴在写controller方法时,要遵循一定的约束(比如:controller方法必须通过Body接收参数,且Body参数必须放在形参列表的第一位)。

诸如此类,具体需要团队伙伴遵循哪些约束,需要具体看需求和代码逻辑,我们主要有这个意识就行,其他都好说。

以上就是通过SpringAOP切面编程,实现【记录系统操作日志】功能的详细流程,喜欢的话可以留个免费的关注呦~~~

http://www.xdnf.cn/news/1391905.html

相关文章:

  • 基于FPGA的DDR3读写实验学习
  • 《ArkUI 记账本开发:状态管理与数据持久化实现》
  • el-table合并列实例
  • 光谱相机多层镀膜技术如何提高透过率
  • (二)Python语法基础(下)
  • 响应式编程框架Reactor【2】
  • Redis开发06:使用stackexchange.redis库结合WebAPI对redis进行增删改查
  • Vue3 全面介绍
  • 技术SEO修复ROI最大化:有限资源下的优先排序策略
  • 【笔记】Linux高性能网络详解之DPDK
  • uni-app 常用钩子函数:从场景到实战,掌握开发核心
  • 算法题打卡力扣第169题:多数元素(easy)
  • 单点登录(SSO)前端(Vue2.X)改造
  • MYSQL-索引(上)
  • week5-[二维数组]对角线
  • 平安健康平安芯医AI解析:7×24小时问诊+95%诊断准确率,人文温度短板与医生效能提升引热议
  • DNS域名系统
  • Less嵌套写法
  • 无人机中的坐标系理解:机体坐标系,东北天(NED)坐标系,世界大地(WGS84)坐标系
  • 换公司如何快速切入软件项目工程
  • 在 Ubuntu 24.04 Linux 上安装 Basemark GPU Benchmark 的步骤
  • PCIe 6.0配置与地址空间架构:深入解析设备初始化的核心机制
  • 零知开源——基于STM32F407VET6和ADXL345三轴加速度计的精准运动姿态检测系统
  • Vibe Coding、AI IDE/插件
  • Vue3 + TS + MapboxGL.js 三维地图开发项目
  • 前端缓存问题详解
  • Prometheus+Grafana入门教程:从零搭建云原生服务器监控系统
  • 【论文阅读】SegCLIP:用于高分辨率遥感图像语义分割的多模态视觉语言和快速学习
  • 【完整源码+数据集+部署教程】控制台缺陷检测系统源码和数据集:改进yolo11-repvit
  • Vision Transformer模型解读