一、需求分析 1.1目标 -记录用户在系统中的关键操作行为 -提供操作追溯和审计功能 -支持问题排查和系统优化 -满足合规性要求 1.2需记录的操作类型 -用户登录/登出 -商品管理操作(增删改查) -订单处理操作(创建、修改、取消、发货等) -库存管理操作 -
一、需求分析
1.1 目标
- 记录用户在系统中的关键操作行为
- 提供操作追溯和审计功能
- 支持问题排查和系统优化
- 满足合规性要求
1.2 需记录的操作类型
- 用户登录/登出
- 商品管理操作(增删改查)
- 订单处理操作(创建、修改、取消、发货等)
- 库存管理操作
- 权限变更操作
- 系统配置修改
- 数据导出操作
二、系统设计
2.1 日志表结构设计
```sql
CREATE TABLE `user_operation_logs` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键ID,
`user_id` bigint(20) NOT NULL COMMENT 用户ID,
`username` varchar(50) NOT NULL COMMENT 用户名,
`operation_type` varchar(50) NOT NULL COMMENT 操作类型,
`operation_module` varchar(50) NOT NULL COMMENT 操作模块,
`operation_content` text COMMENT 操作内容(JSON格式),
`request_params` text COMMENT 请求参数(JSON格式),
`response_result` text COMMENT 响应结果(JSON格式),
`ip_address` varchar(50) COMMENT IP地址,
`user_agent` varchar(500) COMMENT 用户代理,
`operation_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 操作时间,
`execution_time` int(11) COMMENT 执行耗时(毫秒),
`status` tinyint(1) DEFAULT 1 COMMENT 状态(1-成功,0-失败),
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_operation_time` (`operation_time`),
KEY `idx_operation_type` (`operation_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT=用户操作日志表;
```
2.2 技术选型
- 日志记录方式:AOP切面编程 + 注解方式
- 日志存储:MySQL数据库 + 异步写入(提高性能)
- 日志查询:提供管理后台查询界面
- 日志清理:定期归档或删除旧日志
三、开发实现
3.1 自定义日志注解
```java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
String operationType() default ""; // 操作类型
String operationModule() default ""; // 操作模块
boolean saveParams() default true; // 是否保存请求参数
boolean saveResult() default false; // 是否保存响应结果
String desc() default ""; // 操作描述
}
```
3.2 AOP切面实现
```java
@Aspect
@Component
public class OperationLogAspect {
@Autowired
private UserOperationLogService logService;
@Autowired
private HttpServletRequest request;
@Pointcut("@annotation(com.kuailu.annotation.OperationLog)")
public void logPointCut() {}
@Around("logPointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
long beginTime = System.currentTimeMillis();
// 获取方法签名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 获取注解信息
OperationLog operationLog = method.getAnnotation(OperationLog.class);
// 执行方法
Object result = joinPoint.proceed();
// 执行耗时
long time = System.currentTimeMillis() - beginTime;
// 保存日志
try {
saveLog(joinPoint, operationLog, time, result);
} catch (Exception e) {
// 记录日志保存失败,不影响主流程
log.error("保存操作日志失败", e);
}
return result;
}
private void saveLog(ProceedingJoinPoint joinPoint, OperationLog operationLog,
long time, Object result) {
// 获取请求参数
Object[] args = joinPoint.getArgs();
String params = operationLog.saveParams() ? JsonUtils.toJson(args) : null;
// 获取响应结果
String resultStr = operationLog.saveResult() ? JsonUtils.toJson(result) : null;
// 获取用户信息
UserDTO user = UserContext.getCurrentUser();
// 构建日志对象
UserOperationLog log = new UserOperationLog();
log.setUserId(user != null ? user.getId() : 0);
log.setUsername(user != null ? user.getUsername() : "system");
log.setOperationType(operationLog.operationType());
log.setOperationModule(operationLog.operationModule());
log.setOperationContent(operationLog.desc());
log.setRequestParams(params);
log.setResponseResult(resultStr);
log.setIpAddress(getIpAddress());
log.setUserAgent(request.getHeader("User-Agent"));
log.setOperationTime(new Date());
log.setExecutionTime((int)time);
log.setStatus(time > 5000 ? 0 : 1); // 超过5秒视为失败
// 异步保存日志
logService.asyncSaveLog(log);
}
private String getIpAddress() {
// 获取IP地址逻辑
// ...
}
}
```
3.3 异步日志服务
```java
@Service
public class UserOperationLogServiceImpl implements UserOperationLogService {
@Autowired
private UserOperationLogMapper logMapper;
@Async("logTaskExecutor") // 使用异步线程池
@Override
public void asyncSaveLog(UserOperationLog log) {
logMapper.insert(log);
}
// 日志查询方法...
}
```
3.4 线程池配置
```java
@Configuration
public class AsyncTaskConfig {
@Bean("logTaskExecutor")
public Executor logTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("log-task-");
executor.initialize();
return executor;
}
}
```
四、管理后台实现
4.1 日志查询界面
- 支持按时间范围查询
- 支持按用户、操作类型、模块筛选
- 支持查看操作详情(参数、结果等)
- 支持导出Excel
4.2 关键代码示例
```java
@RestController
@RequestMapping("/api/operation-logs")
public class OperationLogController {
@Autowired
private UserOperationLogService logService;
@GetMapping("/page")
public Result
> queryPage(
@RequestParam(required = false) Long userId,
@RequestParam(required = false) String operationType,
@RequestParam(required = false) String operationModule,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List logs = logService.queryLogs(
userId, operationType, operationModule, startTime, endTime);
return Result.success(new PageInfo<>(logs));
}
@GetMapping("/export")
public void exportLogs(HttpServletResponse response,
@RequestParam(required = false) Long userId,
@RequestParam(required = false) String operationType,
@RequestParam(required = false) String operationModule,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime) throws IOException {
List logs = logService.queryLogs(
userId, operationType, operationModule, startTime, endTime);
// 导出Excel逻辑
// ...
}
}
```
五、性能优化与注意事项
1. 异步写入:采用异步方式写入日志,避免影响主业务流程
2. 批量插入:对于高并发场景,可考虑批量插入日志
3. 日志分级:根据操作重要性设置不同日志级别
4. 敏感信息处理:对日志中的敏感信息进行脱敏处理
5. 日志清理:设置日志保留策略,定期清理或归档旧日志
6. 错误处理:确保日志记录失败不影响主业务流程
7. 监控告警:对异常操作或高频失败操作设置监控告警
六、测试验证
1. 单元测试:验证AOP切面和日志服务
2. 集成测试:验证完整操作流程中的日志记录
3. 性能测试:验证高并发下的日志记录性能
4. 异常测试:验证日志记录失败时的系统行为
七、部署与维护
1. 部署:将日志服务与主应用一起部署
2. 监控:监控日志写入性能和失败情况
3. 维护:定期检查日志表大小,执行维护操作
通过以上设计和实现,快驴生鲜系统可以完整记录用户操作日志,为系统运维、审计和问题排查提供有力支持。