# @Log 注解使用指南

## 一、概述

`@Log` 注解是 skills 项目中用于记录操作日志的核心功能。它基于 **AOP（面向切面编程）** 和 **拦截器** 机制实现，能够自动捕获 Controller 层方法的请求和响应信息，并异步保存到数据库的 `sys_log` 表中。

## 二、核心特性

✅ **无侵入性**: 只需添加 `@Log` 注解，无需修改业务代码  
✅ **自动化**: 请求/响应信息自动捕获，无需手动记录  
✅ **高性能**: 异步保存，不影响接口响应速度  
✅ **智能化**: 自动识别浏览器、操作系统、IP等信息  
✅ **可扩展**: 支持类级别和方法级别的注解配置  

## 三、快速开始

### 3.1 数据库初始化

执行 SQL 脚本创建或更新日志表：

```bash
# 在项目根目录执行
mysql -u root -p skills < backend/db/alter_sys_log_table.sql
```

### 3.2 在控制器上添加注解

#### 方式一：类级别注解（推荐）

在 Controller 类上添加 `@Log` 注解，该类下所有方法都会记录日志：

```java
@Log(module = "用户管理")
@RestController
@RequestMapping("/api/user")
public class UserController {
    
    @PostMapping("/add")
    public CommonResult addUser(@RequestBody UserReq req) {
        // 自动记录日志
    }
}
```

#### 方式二：方法级别注解

在特定方法上添加 `@Log` 注解，只记录该方法的日志：

```java
@RestController
@RequestMapping("/api/order")
public class OrderController {
    
    @Log(module = "订单管理", description = "创建订单")
    @PostMapping("/create")
    public CommonResult createOrder(@RequestBody OrderReq req) {
        // 自动记录日志
    }
}
```

#### 方式三：忽略特定接口

对于高频调用的接口，可以设置 `ignore = true` 不记录日志：

```java
@Log(module = "认证")
@RestController
@RequestMapping("/api/auth")
public class AuthController {
    
    @PostMapping("/login")
    public CommonResult login(@RequestBody LoginReq req) {
        // 会记录日志
    }
    
    @Log(ignore = true)
    @GetMapping("/check/token")
    public CommonResult checkToken(@RequestParam String token) {
        // 不会记录日志（高频调用接口）
    }
}
```

## 四、注解属性说明

| 属性 | 类型 | 说明 | 默认值 | 示例 |
|------|------|------|--------|------|
| module | String | 模块名称 | 空字符串 | "用户管理" |
| description | String | 日志描述 | 空字符串 | "创建用户" |
| ignore | boolean | 是否忽略记录 | false | true/false |

## 五、已添加注解的控制器

以下控制器已经添加了 `@Log` 注解：

1. **LoginController** - 登录认证模块
   - 路径: `/api/login/**`
   - 模块名: "登录认证"

2. **CmsContentController** - 内容管理模块
   - 路径: `/api/cmsContent/**`
   - 模块名: "内容管理"

3. **AccountController** - 账户管理模块
   - 路径: `/api/account/**`
   - 模块名: "账户管理"

4. **AccountFrozenController** - 账户冻结模块
   - 路径: `/api/accountFrozen/**`
   - 模块名: "账户冻结"

## 六、日志记录内容

系统会自动记录以下信息：

### 6.1 请求信息
- 请求方法（GET、POST等）
- 请求URL
- 请求头
- 请求体
- 客户端IP
- IP归属地
- 浏览器信息
- 操作系统

### 6.2 响应信息
- 响应状态码
- 响应头
- 响应体（如果可用）
- 执行耗时（毫秒）

### 6.3 其他信息
- 模块名称
- 日志描述
- 成功/失败状态
- 错误信息（如果失败）
- 创建时间

## 七、日志查询

### 7.1 数据库查询

```sql
-- 查询最近的10条日志
SELECT * FROM sys_log ORDER BY create_time DESC LIMIT 10;

-- 按模块查询
SELECT * FROM sys_log WHERE module = '登录认证' ORDER BY create_time DESC;

-- 查询失败的日志
SELECT * FROM sys_log WHERE status = 2 ORDER BY create_time DESC;

-- 按IP查询
SELECT * FROM sys_log WHERE ip = '192.168.1.100' ORDER BY create_time DESC;

-- 按时间范围查询
SELECT * FROM sys_log 
WHERE create_time BETWEEN '2026-04-14 00:00:00' AND '2026-04-14 23:59:59'
ORDER BY create_time DESC;
```

### 7.2 通过 API 查询

可以使用现有的 SysLogController 提供的接口查询日志：

```bash
# 分页查询
POST /api/sysLog/getPageList
{
  "pageNum": 1,
  "pageSize": 10,
  "module": "登录认证",
  "status": 1
}
```

## 八、性能优化

### 8.1 异步保存

日志保存采用异步方式，不会阻塞主业务流程：

```java
@Async
public void saveLogAsync(LogRecord logRecord) {
    // 异步保存逻辑
}
```

### 8.2 数据库索引

日志表已添加以下索引以优化查询性能：

- `idx_module`: 模块查询优化
- `idx_ip`: IP查询优化
- `idx_create_time`: 时间范围查询优化
- `idx_status`: 状态查询优化

### 8.3 定期清理

建议定期清理历史日志，避免数据量过大：

```sql
-- 保留最近6个月的日志
DELETE FROM sys_log WHERE create_time < DATE_SUB(NOW(), INTERVAL 6 MONTH);
```

## 九、常见问题

### 9.1 日志未记录

**可能原因**:
1. 方法标注了 `@Log(ignore = true)`
2. 异步线程池配置问题

**排查步骤**:
- 检查控制器是否添加了 `@Log` 注解
- 查看应用日志是否有错误信息
- 确认 `@EnableAsync` 已启用

### 9.2 响应体为空

**原因**: 拦截器无法直接读取响应体

**解决方案**: 
- 当前版本暂时不记录响应体
- 如需记录，需要使用 ContentCachingResponseWrapper

### 9.3 用户ID为空

**原因**: 尚未实现从 Token 解析用户ID的逻辑

**解决方案**: 
- 在 LogInterceptor 的 `saveLogAsync` 方法中添加用户ID解析逻辑
- 根据实际使用的认证框架（如 Sa-Token）实现

## 十、扩展开发

### 10.1 添加用户ID解析

在 `LogInterceptor.saveLogAsync` 方法中添加：

```java
// 从 Token 中解析用户ID
try {
    String token = request.getHeaders().get("Authorization");
    if (token != null && token.startsWith("Bearer ")) {
        token = token.substring(7);
        // 使用 Sa-Token 解析用户ID
        Object loginId = StpUtil.getLoginIdByToken(token);
        if (loginId != null) {
            sysLog.setCreateUser(Long.parseLong(loginId.toString()));
        }
    }
} catch (Exception e) {
    logger.warn("解析用户ID失败: {}", e.getMessage());
}
```

### 10.2 自定义日志过滤

可以在 `LogInterceptor.preHandle` 方法中添加自定义过滤逻辑：

```java
// 排除敏感接口
String requestUri = request.getRequestURI();
if (requestUri.contains("/sensitive/")) {
    return true;  // 不记录日志
}
```

## 十一、技术架构

### 11.1 核心组件

```
┌─────────────────────────────────────┐
│      Controller (@Log 注解)          │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│    LogInterceptor (拦截器)           │
│  - preHandle: 捕获请求信息           │
│  - afterCompletion: 捕获响应信息     │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│  LogInterceptor.saveLogAsync         │
│  (@Async 异步保存)                   │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│    SysLogMapper (持久层)             │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│    sys_log (MySQL 数据库表)          │
└─────────────────────────────────────┘
```

### 11.2 相关文件

| 文件路径 | 说明 |
|---------|------|
| `annotation/Log.java` | @Log 注解定义 |
| `entity/LogRecord.java` | 日志记录对象 |
| `entity/SysLog.java` | 日志实体类 |
| `interceptor/LogInterceptor.java` | 日志拦截器 |
| `config/LogConfiguration.java` | 日志配置类 |
| `mapper/SysLogMapper.java` | 日志 Mapper 接口 |
| `resources/mapper/SysLogMapper.xml` | 日志 SQL 映射 |
| `db/alter_sys_log_table.sql` | 数据库建表脚本 |

## 十二、总结

`@Log` 注解为 skills 项目提供了强大的操作日志记录功能，具有以下优势：

- ✅ **简单易用**: 只需添加注解即可启用
- ✅ **性能优异**: 异步保存，不影响业务性能
- ✅ **信息完整**: 自动捕获请求/响应的详细信息
- ✅ **灵活配置**: 支持类级别和方法级别的配置
- ✅ **易于扩展**: 可以根据需求自定义日志记录逻辑

通过合理使用 `@Log` 注解，可以实现：
- 操作审计追溯
- 安全事件监控
- 接口调用统计
- 问题排查分析

---

**文档版本**: v1.0  
**最后更新**: 2026-04-14  
**作者**: Lingma AI Assistant
