一、需求分析 1.日志记录范围: -用户登录/登出操作 -商品浏览、搜索、筛选操作 -购物车操作(添加、修改数量、删除) -订单创建、支付、取消操作 -售后申请、退款操作 -用户信息修改操作 2.日志内容要求: -操作时间戳(精确到毫秒) -用户ID/用户名 -
一、需求分析
1. 日志记录范围:
- 用户登录/登出操作
- 商品浏览、搜索、筛选操作
- 购物车操作(添加、修改数量、删除)
- 订单创建、支付、取消操作
- 售后申请、退款操作
- 用户信息修改操作
2. 日志内容要求:
- 操作时间戳(精确到毫秒)
- 用户ID/用户名
- 操作类型
- 操作对象(如商品ID、订单号等)
- 操作结果(成功/失败)
- 操作详情(如修改前的值、修改后的值)
- 客户端信息(IP地址、设备类型、浏览器类型等)
二、技术方案设计
1. 日志存储方案
- 数据库存储:使用MySQL表存储结构化日志
- 文件存储:同时写入日志文件供离线分析
- ELK集成:考虑未来扩展,预留Elasticsearch接口
2. 日志表设计
```sql
CREATE TABLE user_operation_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
username VARCHAR(50),
operation_type VARCHAR(50) NOT NULL,
operation_object VARCHAR(100),
operation_result VARCHAR(20) NOT NULL,
operation_detail TEXT,
client_ip VARCHAR(50),
user_agent VARCHAR(255),
create_time DATETIME(3) NOT NULL,
INDEX idx_user_id (user_id),
INDEX idx_operation_time (create_time),
INDEX idx_operation_type (operation_type)
);
```
3. 实现方式
- AOP切面实现:使用Spring AOP拦截关键方法
- 注解方式:自定义@OperationLog注解标记需要记录的方法
- 异步写入:使用消息队列(如RabbitMQ)异步处理日志写入
三、具体实现代码
1. 自定义注解
```java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
String operationType() default "";
String operationObject() default "";
String detail() default "";
}
```
2. AOP切面实现
```java
@Aspect
@Component
public class OperationLogAspect {
@Autowired
private OperationLogService logService;
@Autowired
private HttpServletRequest request;
@Around("@annotation(operationLog)")
public Object around(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
// 获取用户信息
Long userId = getCurrentUserId();
String username = getCurrentUsername();
// 获取客户端信息
String ip = getClientIp();
String userAgent = request.getHeader("User-Agent");
// 记录开始时间
long startTime = System.currentTimeMillis();
// 执行原方法
Object result = joinPoint.proceed();
// 计算耗时
long costTime = System.currentTimeMillis() - startTime;
// 构建日志对象
OperationLogDTO logDTO = new OperationLogDTO();
logDTO.setUserId(userId);
logDTO.setUsername(username);
logDTO.setOperationType(operationLog.operationType());
logDTO.setOperationObject(operationLog.operationObject());
logDTO.setOperationResult(result == null ? "失败" : "成功");
logDTO.setOperationDetail(operationLog.detail());
logDTO.setClientIp(ip);
logDTO.setUserAgent(userAgent);
logDTO.setCostTime(costTime);
// 异步保存日志
logService.saveAsync(logDTO);
return result;
}
// 其他辅助方法...
}
```
3. 日志服务实现
```java
@Service
public class OperationLogServiceImpl implements OperationLogService {
@Autowired
private OperationLogMapper logMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
@Override
public void save(OperationLogDTO logDTO) {
// 直接写入数据库
OperationLogEntity entity = convertToEntity(logDTO);
logMapper.insert(entity);
}
@Override
@Async
public void saveAsync(OperationLogDTO logDTO) {
// 发送到消息队列异步处理
rabbitTemplate.convertAndSend("log.exchange", "log.save", logDTO);
}
// 消息消费者
@RabbitListener(queues = "log.save.queue")
public void consumeLogMessage(OperationLogDTO logDTO) {
OperationLogEntity entity = convertToEntity(logDTO);
logMapper.insert(entity);
}
}
```
四、日志查询功能实现
1. 查询接口:
```java
@RestController
@RequestMapping("/api/logs")
public class LogController {
@Autowired
private LogQueryService logQueryService;
@GetMapping
public Result
> queryLogs(
@RequestParam(required = false) Long userId,
@RequestParam(required = false) String operationType,
@RequestParam(required = false) Date startTime,
@RequestParam(required = false) Date endTime,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
Page page = logQueryService.queryLogs(
userId, operationType, startTime, endTime, pageNum, pageSize);
return Result.success(page);
}
}
```
2. 查询服务:
```java
@Service
public class LogQueryServiceImpl implements LogQueryService {
@Autowired
private OperationLogMapper logMapper;
@Override
public Page queryLogs(Long userId, String operationType,
Date startTime, Date endTime,
Integer pageNum, Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List entities = logMapper.selectByCondition(
userId, operationType, startTime, endTime);
List vos = entities.stream()
.map(this::convertToVO)
.collect(Collectors.toList());
return new Page<>(vos);
}
}
```
五、测试与验证
1. 单元测试:
- 测试日志记录是否正常写入
- 测试异常情况下的日志记录
- 测试异步日志处理是否正确
2. 集成测试:
- 模拟用户操作,验证日志是否完整记录
- 测试高并发情况下的日志处理能力
3. 性能测试:
- 测试日志写入对系统性能的影响
- 测试日志查询的响应时间
六、部署与监控
1. 日志轮转配置:
- 配置日志文件大小限制和保留天数
- 设置日志文件压缩和归档策略
2. 监控告警:
- 监控日志写入延迟
- 设置日志写入失败告警
- 监控日志查询接口响应时间
七、优化方向
1. 日志分级:根据操作重要性设置不同日志级别
2. 采样记录:对高频操作进行采样记录,减少存储压力
3. 日志分析:开发日志分析功能,生成用户行为报告
4. 数据脱敏:对敏感信息进行脱敏处理
八、开发注意事项
1. 确保日志记录不影响主业务流程性能
2. 考虑日志存储的空间占用,设置合理的保留策略
3. 保护用户隐私,避免记录敏感信息
4. 考虑分布式系统下的日志收集问题
5. 提供日志清理和归档功能
通过以上方案,快驴生鲜系统能够完整记录用户操作日志,为系统运维、安全审计和用户行为分析提供有力支持。