综合档案管理系统审计跟踪功能开发实操指南

一、数据库表结构设计与初始化

实现审计跟踪的第一步是建立能够存储详细操作日志的数据表。该表需要记录操作人、时间、IP、模块、操作类型、具体参数以及执行结果。以下是基于MySQL的完整建表语句,包含所有必要字段,直接执行即可。

```sql CREATE TABLE `sys_audit_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `module_name` varchar(50) NOT NULL COMMENT '模块名称', `action_name` varchar(50) NOT NULL COMMENT '操作名称', `method_name` varchar(100) NOT NULL COMMENT '执行方法名', `user_id` bigint(20) DEFAULT NULL COMMENT '操作人ID', `username` varchar(50) DEFAULT NULL COMMENT '操作人账号', `ip_address` varchar(50) DEFAULT NULL COMMENT '请求IP地址', `request_params` longtext COMMENT '请求参数', `response_result` longtext COMMENT '返回结果', `status` tinyint(2) DEFAULT '1' COMMENT '状态:1成功,0失败', `error_msg` varchar(500) DEFAULT NULL COMMENT '错误信息', `execute_time` bigint(20) DEFAULT NULL COMMENT '执行时长(毫秒)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), KEY `idx_create_time` (`create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统审计跟踪日志表'; ```

二、项目核心依赖配置

本指南基于Spring Boot 2.7.x版本,使用AOP进行切面日志采集,使用MyBatis-Plus进行数据持久化。请确保在pom.xml中引入以下依赖。不要遗漏fastjson,它用于参数和结果的JSON序列化。

```xml org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-aop com.baomidou mybatis-plus-boot-starter 3.5.2 mysql mysql-connector-java runtime com.alibaba fastjson 1.2.83 org.projectlombok lombok true ```

三、自定义审计注解定义

为了方便在业务代码中标记需要审计的方法,我们需要定义一个自定义注解@AuditLog。通过该注解,开发者可以声明当前操作的模块名称和具体动作,例如“档案管理-上传文件”。

创建包com.example.archive.annotation,并写入以下代码:

```java package com.example.archive.annotation; import java.lang.annotation.; @Target(ElementType.METHOD) // 作用在方法上 @Retention(RetentionPolicy.RUNTIME) // 运行时生效 @Documented public @interface AuditLog { / 模块名称,如:档案管理 / String module() default ""; / 操作名称,如:新增档案 / String action() default ""; } ```

四、AOP切面核心逻辑实现

这是审计功能的核心。切面会拦截所有带有@AuditLog注解的方法,捕获方法执行前后的参数、返回值、异常以及执行耗时。请确保IP获取工具类正确配置,否则记录的IP将为空。

创建包com.example.archive.aspect,创建AuditLogAspect类:

```java package com.example.archive.aspect; import com.alibaba.fastjson.JSON; import com.example.archive.annotation.AuditLog; import com.example.common.util.IpUtil; // 假设你有IP工具类,若没有需自行实现获取Request逻辑 import com.example.entity.SysAuditLog; import com.example.service.SysAuditLogService; import org.aspectj.lang.ProceedingJoinPoint; org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContextHolder; // 假设使用Spring Security import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.Date; @Aspect @Component public class AuditLogAspect { @Autowired private SysAuditLogService auditLogService; @Around("@annotation(auditLog)") public Object around(ProceedingJoinPoint point, AuditLog auditLog) throws Throwable { long beginTime = System.currentTimeMillis(); Object result = null; Exception exception = null; // 1. 执行业务逻辑 try { result = point.proceed(); return result; } catch (Exception e) { exception = e; throw e; // 异常必须抛出,否则事务无法回滚 } finally { long endTime = System.currentTimeMillis(); saveLog(point, auditLog, result, exception, endTime - beginTime); } } private void saveLog(ProceedingJoinPoint point, AuditLog auditLog, Object result, Exception exception, long time) { MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); SysAuditLog logEntity = new SysAuditLog(); // 设置注解上的模块和动作 logEntity.setModuleName(auditLog.module()); logEntity.setActionName(auditLog.action()); logEntity.setMethodName(method.getDeclaringClass().getName() + "." + method.getName()); // 获取请求参数 Object[] args = point.getArgs(); try { String params = JSON.toJSONString(args); // 防止参数过长导致数据库存储失败,可根据实际情况截断 if (params.length() > 2000) { params = params.substring(0, 2000); } logEntity.setRequestParams(params); } catch (Exception e) { logEntity.setRequestParams("参数解析异常"); } // 设置返回结果 if (result != null) { try { String response = JSON.toJSONString(result); if (response.length() > 2000) { response = response.substring(0, 2000); } logEntity.setResponseResult(response); } catch (Exception e) { logEntity.setResponseResult("结果解析异常"); } } // 设置状态与异常信息 if (exception != null) { logEntity.setStatus(0); logEntity.setErrorMsg(exception.getMessage()); } else { logEntity.setStatus(1); } logEntity.setExecuteTime(time); // 获取当前用户信息 (需根据你的项目认证体系调整,此处为示例) try { // String username = SecurityContextHolder.getContext().getAuthentication().getName(); // logEntity.setUsername(username); // 临时模拟,实际请替换为获取当前登录用户的逻辑 logEntity.setUsername("admin"); logEntity.setUserId(1L); } catch (Exception e) { // 忽略获取用户失败,防止日志影响业务 } // 获取IP地址 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); logEntity.setIpAddress(request.getRemoteAddr()); logEntity.setCreateTime(new Date()); // 异步保存日志 auditLogService.saveLogAsync(logEntity); } } ```

五、实体类与异步持久化服务

综合档案管理系统审计跟踪功能开发实操指南

为了保证审计日志的记录不影响主业务的响应速度,日志的入库操作必须异步执行。我们需要配置线程池并编写Service层代码。

在启动类或配置类上开启异步支持:

```java @EnableAsync @SpringBootApplication public class ArchiveApplication { public static void main(String[] args) { SpringApplication.run(ArchiveApplication.class, args); } // 配置异步线程池,防止使用默认线程池导致OOM @Bean(name = "auditTaskExecutor") public Executor auditTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(2); executor.setMaxPoolSize(5); executor.setQueueCapacity(100); executor.setThreadNamePrefix("Audit-Log-"); executor.initialize(); return executor; } } ```

接着,编写SysAuditLogService,使用@Async注解指定线程池:

```java package com.example.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.entity.SysAuditLog; import com.example.mapper.SysAuditLogMapper; import com.example.service.SysAuditLogService; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class SysAuditLogServiceImpl extends ServiceImpl implements SysAuditLogService { @Override @Async("auditTaskExecutor") // 指定上面配置的线程池 public void saveLogAsync(SysAuditLog logEntity) { // 这里直接调用MyBatis Plus的save方法 this.save(logEntity); } } ```

六、业务层集成实操

完成上述配置后,在档案管理的Controller或Service方法上,只需添加@AuditLog注解即可自动生成审计日志。

以下是一个档案上传和删除的Controller示例:

```java package com.example.controller; import com.example.archive.annotation.AuditLog; import org.springframework.web.bind.annotation.; import org.springframework.web.multipart.MultipartFile; @RestController @RequestMapping("/api/archive") public class ArchiveController { @PostMapping("/upload") // 关键点:添加注解,声明这是“档案管理”模块的“上传文件”操作 @AuditLog(module = "档案管理", action = "上传文件") public String uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("category") String category) { // 模拟业务逻辑 if (file == null) { throw new RuntimeException("文件不能为空"); } // ... 执行保存逻辑 ... return "文件上传成功,ID: " + System.currentTimeMillis(); } @DeleteMapping("/delete/{id}") // 关键点:添加注解,声明这是“档案管理”模块的“删除档案”操作 @AuditLog(module = "档案管理", action = "删除档案") public String deleteArchive(@PathVariable("id") Long id) { // 模拟业务逻辑 if (id == 1) { throw new RuntimeException("系统核心档案禁止删除"); } // ... 执行删除逻辑 ... return "档案ID: " + id + " 已删除"; } } ```

七、功能验证与排查

部署完成后,通过Postman或浏览器调用上述接口,观察数据库sys_audit_log表的数据变化。

  • 成功场景验证:调用/upload接口并传入正常文件。检查数据库,应生成一条status=1的记录,request_params包含文件名和分类,response_result包含返回ID。
  • 失败场景验证:调用/delete/1接口。检查数据库,应生成一条status=0的记录,error_msg字段应准确显示“系统核心档案禁止删除”。
  • 性能验证:在Service的saveLogAsync方法中打断点或让线程休眠2秒,再次调用接口,确认前端响应不需要等待这2秒,证明异步配置生效。
AI咨询
热线电话

028-85154420

15388110056

全国售前咨询电话

扫码咨询
安答联动微信公众号二维码

微信扫码关注安答联动

申请试用
热线电话
申请试用

安答联动档案管理系统