# 操作日志使用说明 ## 1. 功能概述 系统集成了完整的操作日志记录功能,自动记录用户的所有重要操作,包括: - **登录日志**:用户登录、登出操作 - **操作日志**:用户的增删改查等业务操作 ## 2. 数据库表结构 ### 2.1 sys_log 表 ```sql CREATE TABLE `sys_log` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '日志ID', `user_id` BIGINT(20) DEFAULT NULL COMMENT '用户ID', `username` VARCHAR(50) DEFAULT NULL COMMENT '用户名', `real_name` VARCHAR(50) DEFAULT NULL COMMENT '真实姓名', `log_type` VARCHAR(20) NOT NULL COMMENT '日志类型:登录日志、操作日志', `operate_type` VARCHAR(50) DEFAULT NULL COMMENT '操作类型:增删改查等', `detail` VARCHAR(500) DEFAULT NULL COMMENT '日志详情描述', `controller` VARCHAR(100) DEFAULT NULL COMMENT '控制器名称', `method` VARCHAR(100) DEFAULT NULL COMMENT '方法名称', `request_url` VARCHAR(255) DEFAULT NULL COMMENT '请求URL', `request_method` VARCHAR(10) DEFAULT NULL COMMENT '请求方式:GET/POST/PUT/DELETE', `request_params` TEXT DEFAULT NULL COMMENT '请求参数', `operate_ip` VARCHAR(50) DEFAULT NULL COMMENT '操作IP地址', `operate_location` VARCHAR(100) DEFAULT NULL COMMENT '操作地点', `operate_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间', `use_time` BIGINT(20) DEFAULT NULL COMMENT '执行时长(毫秒)', `status` TINYINT(1) DEFAULT 1 COMMENT '状态:0-失败,1-成功', `error_msg` TEXT DEFAULT NULL COMMENT '错误信息', `browser` VARCHAR(100) DEFAULT NULL COMMENT '浏览器', `os` VARCHAR(100) DEFAULT NULL COMMENT '操作系统', PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), KEY `idx_operate_time` (`operate_time`), KEY `idx_log_type` (`log_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统操作日志表'; ``` ## 3. 使用方法 ### 3.1 在Controller方法上添加注解 在需要记录日志的方法上添加 `@SystemLogHandler` 注解: ```java @RestController @RequestMapping("/system/user") public class UserController { /** * 新增用户 * 注解格式:@SystemLogHandler("操作描述|操作类型") */ @SystemLogHandler("新增用户|新增") @PostMapping("/add") public RestResult add(@RequestBody SysUser user) { userService.save(user); return RestResult.ok("新增成功"); } /** * 修改用户 */ @SystemLogHandler("修改用户|修改") @PutMapping("/edit") public RestResult edit(@RequestBody SysUser user) { userService.updateById(user); return RestResult.ok("修改成功"); } /** * 删除用户 */ @SystemLogHandler("删除用户|删除") @DeleteMapping("/remove/{id}") public RestResult remove(@PathVariable Long id) { userService.removeById(id); return RestResult.ok("删除成功"); } /** * 查询用户列表 */ @SystemLogHandler("查询用户列表|查询") @GetMapping("/list") public RestResult list() { List users = userService.list(); return RestResult.ok(users); } /** * 导出用户数据 */ @SystemLogHandler("导出用户数据|导出") @GetMapping("/export") public void export(HttpServletResponse response) { // 导出逻辑 } } ``` ### 3.2 注解格式说明 ```java @SystemLogHandler("操作描述|操作类型") ``` - **操作描述**:中文描述,说明这个操作做了什么(如:新增用户、修改角色、删除菜单) - **操作类型**:操作分类(如:新增、修改、删除、查询、导出、登录、登出) - 使用 `|` 分隔两部分 **特殊处理**: - 如果操作类型是 `登录` 或 `登出`,日志类型自动标记为 `登录日志` - 其他操作类型,日志类型自动标记为 `操作日志` ### 3.3 示例:登录/登出 ```java @RestController @RequestMapping("/auth") public class SysUserController { /** * 用户登录 */ @SystemLogHandler("用户登录|登录") @PostMapping("/login") public RestResult login(@RequestBody LoginBody loginBody) { // 登录逻辑 } /** * 用户登出 */ @SystemLogHandler("用户登出|登出") @PostMapping("/logout") public RestResult logout() { // 登出逻辑 } /** * 手机号验证码登录 */ @SystemLogHandler("手机号验证码登录|登录") @PostMapping("/loginByPhone") public RestResult loginByPhone(@RequestBody LoginBody loginBody) { // 登录逻辑 } } ``` ## 4. 自动记录的信息 ### 4.1 用户信息(从SecurityContext获取) - 用户ID - 用户名 - 真实姓名(昵称) ### 4.2 操作信息 - 日志类型(登录日志/操作日志) - 操作类型(新增/修改/删除等) - 操作描述 - 控制器名称 - 方法名称 ### 4.3 请求信息 - 请求URL - 请求方式(GET/POST/PUT/DELETE) - 请求参数(自动过滤密码字段) ### 4.4 环境信息 - 操作IP地址 - 浏览器类型 - 操作系统 ### 4.5 执行信息 - 操作时间 - 执行时长(毫秒) - 执行状态(成功/失败) - 错误信息(如果失败) ## 5. 敏感信息处理 ### 5.1 密码字段自动过滤 系统自动将请求参数中的 `password` 字段替换为 `******`: ```json // 原始参数 { "username": "admin", "password": "admin123" } // 记录到日志 { "username": "admin", "password": "******" } ``` ### 5.2 参数长度限制 请求参数超过2000字符时,自动截断并添加 `...` 后缀。 ## 6. 性能优化 ### 6.1 异步保存 日志保存使用 `@Async` 异步执行,不影响主业务性能: ```java @Async public void saveLogAsync(SysLog log) { save(log); } ``` ### 6.2 异常隔离 日志保存失败不会影响主业务: ```java try { saveLog(jp, request, startTime, exception); } catch (Exception e) { logger.error("保存操作日志失败", e); } ``` ## 7. 查询日志 ### 7.1 按日志类型查询 ```sql -- 查询登录日志 SELECT * FROM sys_log WHERE log_type = '登录日志' ORDER BY operate_time DESC; -- 查询操作日志 SELECT * FROM sys_log WHERE log_type = '操作日志' ORDER BY operate_time DESC; ``` ### 7.2 按用户查询 ```sql -- 查询某个用户的所有操作 SELECT * FROM sys_log WHERE user_id = 1 ORDER BY operate_time DESC; -- 查询某个用户的登录记录 SELECT * FROM sys_log WHERE user_id = 1 AND log_type = '登录日志' ORDER BY operate_time DESC; ``` ### 7.3 按时间范围查询 ```sql -- 查询今天的操作日志 SELECT * FROM sys_log WHERE DATE(operate_time) = CURDATE() ORDER BY operate_time DESC; -- 查询最近7天的操作日志 SELECT * FROM sys_log WHERE operate_time >= DATE_SUB(NOW(), INTERVAL 7 DAY) ORDER BY operate_time DESC; ``` ### 7.4 按操作类型查询 ```sql -- 查询所有删除操作 SELECT * FROM sys_log WHERE operate_type = '删除' ORDER BY operate_time DESC; -- 查询所有失败的操作 SELECT * FROM sys_log WHERE status = 0 ORDER BY operate_time DESC; ``` ### 7.5 性能分析 ```sql -- 查询执行时间超过1秒的操作 SELECT * FROM sys_log WHERE use_time > 1000 ORDER BY use_time DESC; -- 统计各操作类型的平均执行时间 SELECT operate_type, COUNT(*) as count, AVG(use_time) as avg_time, MAX(use_time) as max_time FROM sys_log GROUP BY operate_type ORDER BY avg_time DESC; ``` ## 8. 日志分析示例 ### 8.1 用户活跃度统计 ```sql -- 统计每个用户的操作次数 SELECT username, real_name, COUNT(*) as operate_count, MAX(operate_time) as last_operate_time FROM sys_log WHERE log_type = '操作日志' GROUP BY user_id, username, real_name ORDER BY operate_count DESC; ``` ### 8.2 登录统计 ```sql -- 统计每个用户的登录次数 SELECT username, COUNT(*) as login_count, MAX(operate_time) as last_login_time FROM sys_log WHERE log_type = '登录日志' AND operate_type = '登录' GROUP BY username ORDER BY login_count DESC; -- 统计每天的登录次数 SELECT DATE(operate_time) as date, COUNT(*) as login_count FROM sys_log WHERE log_type = '登录日志' AND operate_type = '登录' GROUP BY DATE(operate_time) ORDER BY date DESC; ``` ### 8.3 异常操作统计 ```sql -- 统计失败的操作 SELECT username, detail, error_msg, operate_time FROM sys_log WHERE status = 0 ORDER BY operate_time DESC LIMIT 100; -- 统计失败次数最多的操作 SELECT detail, COUNT(*) as fail_count FROM sys_log WHERE status = 0 GROUP BY detail ORDER BY fail_count DESC; ``` ## 9. 注意事项 ### 9.1 性能考虑 1. **异步保存**:日志使用异步方式保存,不影响主业务性能 2. **批量查询**:查询大量日志时,建议添加时间范围限制 3. **定期清理**:建议定期清理历史日志(如保留最近6个月) ### 9.2 安全考虑 1. **敏感信息**:密码字段自动过滤,不会记录到日志 2. **权限控制**:日志查询接口需要添加权限控制 3. **数据脱敏**:如果日志包含其他敏感信息,需要手动脱敏 ### 9.3 最佳实践 1. **合理使用注解**:只在重要操作上添加 `@SystemLogHandler` 注解 2. **清晰的描述**:操作描述要清晰明了,便于日后追踪 3. **统一命名**:操作类型使用统一的命名(新增、修改、删除、查询、导出等) 4. **及时清理**:定期清理历史日志,保持数据库性能 ## 10. 常见问题 ### Q1: 为什么有些方法没有记录日志? A: 只有添加了 `@SystemLogHandler` 注解的方法才会记录日志。请检查方法是否添加了该注解。 ### Q2: 日志中的用户信息为空? A: 可能的原因: - 用户未登录(匿名访问) - SecurityContext中没有认证信息 - 登录接口因为在认证前执行,可能获取不到用户信息 ### Q3: 如何关闭某个接口的日志记录? A: 移除该方法上的 `@SystemLogHandler` 注解即可。 ### Q4: 日志保存失败怎么办? A: 日志保存失败不会影响主业务,但会在控制台输出错误日志。请检查: - 数据库连接是否正常 - sys_log 表是否存在 - 字段类型是否匹配 ### Q5: 如何自定义日志内容? A: 可以修改 `ControllerAop.java` 中的 `saveLog` 方法,添加自定义字段。 ## 11. 扩展功能 ### 11.1 添加IP地理位置解析 可以集成IP地址库,将IP地址解析为具体地理位置: ```java // 在 IpUtils 中添加方法 public static String getIpLocation(String ip) { // 调用IP地址库API,解析地理位置 // 返回如:"北京市 联通" } // 在 ControllerAop 中使用 log.setOperateLocation(IpUtils.getIpLocation(ipAddr)); ``` ### 11.2 添加数据变更记录 对于重要数据的修改,可以记录修改前后的值: ```java @SystemLogHandler("修改用户|修改") @PutMapping("/edit") public RestResult edit(@RequestBody SysUser user) { // 查询修改前的数据 SysUser oldUser = userService.getById(user.getId()); // 执行修改 userService.updateById(user); // 可以将修改前后的对比记录到日志的备注字段 return RestResult.ok("修改成功"); } ``` ### 11.3 实时日志推送 可以集成WebSocket,将重要操作实时推送到管理员: ```java @Autowired private WebSocketService webSocketService; // 在保存日志后推送 if ("删除".equals(log.getOperateType())) { webSocketService.sendToAdmin("用户执行了删除操作", log); } ``` --- **文档版本**:v1.0 **最后更新**:2025-10-29 **维护者**:开发团队