Commit 132c1cc828334fa303ba53df5bc34ea82d8e5995

Authored by YunaiV
1 parent 9fac998a

【优化】增强访问日志,支持是否记录、脱敏、操作信息等功能

Showing 20 changed files with 484 additions and 297 deletions
sql/mysql/ruoyi-vue-pro.sql
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 Target Server Version : 80200 (8.2.0) 11 Target Server Version : 80200 (8.2.0)
12 File Encoding : 65001 12 File Encoding : 65001
13 13
14 - Date: 30/03/2024 20:42:06 14 + Date: 03/04/2024 19:07:31
15 */ 15 */
16 16
17 SET NAMES utf8mb4; 17 SET NAMES utf8mb4;
@@ -327,9 +327,13 @@ CREATE TABLE `infra_api_access_log` ( @@ -327,9 +327,13 @@ CREATE TABLE `infra_api_access_log` (
327 `application_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名', 327 `application_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '应用名',
328 `request_method` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '请求方法名', 328 `request_method` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '请求方法名',
329 `request_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '请求地址', 329 `request_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '请求地址',
330 - `request_params` varchar(8000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '请求参数', 330 + `request_params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '请求参数',
  331 + `response_body` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT '响应结果',
331 `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户 IP', 332 `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户 IP',
332 `user_agent` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '浏览器 UA', 333 `user_agent` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '浏览器 UA',
  334 + `operate_module` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '操作模块',
  335 + `operate_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '操作名',
  336 + `operate_type` tinyint NULL DEFAULT 0 COMMENT '操作分类',
333 `begin_time` datetime NOT NULL COMMENT '开始请求时间', 337 `begin_time` datetime NOT NULL COMMENT '开始请求时间',
334 `end_time` datetime NOT NULL COMMENT '结束请求时间', 338 `end_time` datetime NOT NULL COMMENT '结束请求时间',
335 `duration` int NOT NULL COMMENT '执行时长', 339 `duration` int NOT NULL COMMENT '执行时长',
@@ -343,7 +347,7 @@ CREATE TABLE `infra_api_access_log` ( @@ -343,7 +347,7 @@ CREATE TABLE `infra_api_access_log` (
343 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 347 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
344 PRIMARY KEY (`id`) USING BTREE, 348 PRIMARY KEY (`id`) USING BTREE,
345 INDEX `idx_create_time`(`create_time` ASC) USING BTREE 349 INDEX `idx_create_time`(`create_time` ASC) USING BTREE
346 -) ENGINE = InnoDB AUTO_INCREMENT = 35832 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表'; 350 +) ENGINE = InnoDB AUTO_INCREMENT = 35920 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表';
347 351
348 -- ---------------------------- 352 -- ----------------------------
349 -- Records of infra_api_access_log 353 -- Records of infra_api_access_log
@@ -385,7 +389,7 @@ CREATE TABLE `infra_api_error_log` ( @@ -385,7 +389,7 @@ CREATE TABLE `infra_api_error_log` (
385 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 389 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
386 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 390 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
387 PRIMARY KEY (`id`) USING BTREE 391 PRIMARY KEY (`id`) USING BTREE
388 -) ENGINE = InnoDB AUTO_INCREMENT = 16429 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志'; 392 +) ENGINE = InnoDB AUTO_INCREMENT = 16462 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
389 393
390 -- ---------------------------- 394 -- ----------------------------
391 -- Records of infra_api_error_log 395 -- Records of infra_api_error_log
@@ -494,7 +498,7 @@ CREATE TABLE `infra_config` ( @@ -494,7 +498,7 @@ CREATE TABLE `infra_config` (
494 -- Records of infra_config 498 -- Records of infra_config
495 -- ---------------------------- 499 -- ----------------------------
496 BEGIN; 500 BEGIN;
497 -INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'sys.user.init-password', '123456', b'0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '1', '2024-02-28 22:54:14', b'0'); 501 +INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'biz', 1, '用户管理-账号初始密码', 'sys.user.init-password', '123456', b'0', '初始化密码 123456', 'admin', '2021-01-05 17:03:48', '1', '2024-04-03 17:22:28', b'0');
498 INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7, 'url', 2, 'MySQL 监控的地址', 'url.druid', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:33:38', b'0'); 502 INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (7, 'url', 2, 'MySQL 监控的地址', 'url.druid', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:33:38', b'0');
499 INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8, 'url', 2, 'SkyWalking 监控的地址', 'url.skywalking', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:57:03', b'0'); 503 INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8, 'url', 2, 'SkyWalking 监控的地址', 'url.skywalking', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:57:03', b'0');
500 INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (9, 'url', 2, 'Spring Boot Admin 监控的地址', 'url.spring-boot-admin', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:52:07', b'0'); 504 INSERT INTO `infra_config` (`id`, `category`, `type`, `name`, `config_key`, `value`, `visible`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (9, 'url', 2, 'Spring Boot Admin 监控的地址', 'url.spring-boot-admin', '', b'1', '', '1', '2023-04-07 13:41:16', '1', '2023-04-07 14:52:07', b'0');
@@ -690,7 +694,7 @@ CREATE TABLE `infra_file` ( @@ -690,7 +694,7 @@ CREATE TABLE `infra_file` (
690 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 694 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
691 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 695 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
692 PRIMARY KEY (`id`) USING BTREE 696 PRIMARY KEY (`id`) USING BTREE
693 -) ENGINE = InnoDB AUTO_INCREMENT = 1301 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表'; 697 +) ENGINE = InnoDB AUTO_INCREMENT = 1302 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
694 698
695 -- ---------------------------- 699 -- ----------------------------
696 -- Records of infra_file 700 -- Records of infra_file
@@ -1416,7 +1420,7 @@ CREATE TABLE `system_login_log` ( @@ -1416,7 +1420,7 @@ CREATE TABLE `system_login_log` (
1416 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 1420 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1417 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 1421 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
1418 PRIMARY KEY (`id`) USING BTREE 1422 PRIMARY KEY (`id`) USING BTREE
1419 -) ENGINE = InnoDB AUTO_INCREMENT = 3054 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录'; 1423 +) ENGINE = InnoDB AUTO_INCREMENT = 3066 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
1420 1424
1421 -- ---------------------------- 1425 -- ----------------------------
1422 -- Records of system_login_log 1426 -- Records of system_login_log
@@ -2453,7 +2457,7 @@ CREATE TABLE `system_oauth2_access_token` ( @@ -2453,7 +2457,7 @@ CREATE TABLE `system_oauth2_access_token` (
2453 PRIMARY KEY (`id`) USING BTREE, 2457 PRIMARY KEY (`id`) USING BTREE,
2454 INDEX `idx_access_token`(`access_token` ASC) USING BTREE, 2458 INDEX `idx_access_token`(`access_token` ASC) USING BTREE,
2455 INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE 2459 INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
2456 -) ENGINE = InnoDB AUTO_INCREMENT = 6332 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌'; 2460 +) ENGINE = InnoDB AUTO_INCREMENT = 6366 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
2457 2461
2458 -- ---------------------------- 2462 -- ----------------------------
2459 -- Records of system_oauth2_access_token 2463 -- Records of system_oauth2_access_token
@@ -2575,7 +2579,7 @@ CREATE TABLE `system_oauth2_refresh_token` ( @@ -2575,7 +2579,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
2575 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 2579 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
2576 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 2580 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
2577 PRIMARY KEY (`id`) USING BTREE 2581 PRIMARY KEY (`id`) USING BTREE
2578 -) ENGINE = InnoDB AUTO_INCREMENT = 1430 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌'; 2582 +) ENGINE = InnoDB AUTO_INCREMENT = 1441 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
2579 2583
2580 -- ---------------------------- 2584 -- ----------------------------
2581 -- Records of system_oauth2_refresh_token 2585 -- Records of system_oauth2_refresh_token
@@ -2615,7 +2619,7 @@ CREATE TABLE `system_operate_log` ( @@ -2615,7 +2619,7 @@ CREATE TABLE `system_operate_log` (
2615 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 2619 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
2616 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号', 2620 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
2617 PRIMARY KEY (`id`) USING BTREE 2621 PRIMARY KEY (`id`) USING BTREE
2618 -) ENGINE = InnoDB AUTO_INCREMENT = 11964 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录'; 2622 +) ENGINE = InnoDB AUTO_INCREMENT = 12000 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
2619 2623
2620 -- ---------------------------- 2624 -- ----------------------------
2621 -- Records of system_operate_log 2625 -- Records of system_operate_log
@@ -5305,7 +5309,7 @@ CREATE TABLE `system_sms_log` ( @@ -5305,7 +5309,7 @@ CREATE TABLE `system_sms_log` (
5305 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', 5309 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
5306 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', 5310 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
5307 PRIMARY KEY (`id`) USING BTREE 5311 PRIMARY KEY (`id`) USING BTREE
5308 -) ENGINE = InnoDB AUTO_INCREMENT = 946 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志'; 5312 +) ENGINE = InnoDB AUTO_INCREMENT = 947 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
5309 5313
5310 -- ---------------------------- 5314 -- ----------------------------
5311 -- Records of system_sms_log 5315 -- Records of system_sms_log
@@ -5475,7 +5479,7 @@ INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `c @@ -5475,7 +5479,7 @@ INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `c
5475 INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (151, '大租户', 126, '土豆大', NULL, 0, 'https://tudou.iocoder.cn', 111, '2023-12-08 00:00:00', 10, '1', '2023-12-02 23:35:05', '1', '2023-12-08 23:39:56', b'0'); 5479 INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (151, '大租户', 126, '土豆大', NULL, 0, 'https://tudou.iocoder.cn', 111, '2023-12-08 00:00:00', 10, '1', '2023-12-02 23:35:05', '1', '2023-12-08 23:39:56', b'0');
5476 INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (152, '新租户', 127, '土豆', NULL, 0, 'http://xx.iocoder.cn', 111, '2025-12-31 00:00:00', 50, '1', '2023-12-30 11:43:17', '1', '2023-12-30 11:43:17', b'0'); 5480 INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (152, '新租户', 127, '土豆', NULL, 0, 'http://xx.iocoder.cn', 111, '2025-12-31 00:00:00', 50, '1', '2023-12-30 11:43:17', '1', '2023-12-30 11:43:17', b'0');
5477 INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (153, '小明的租户', 128, 'xiaoming', '15601691301', 0, 'xiaoming.iocoder.cn', 111, '2025-12-01 00:00:00', 100, '1', '2024-02-27 21:58:25', '1', '2024-02-28 22:53:54', b'0'); 5481 INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (153, '小明的租户', 128, 'xiaoming', '15601691301', 0, 'xiaoming.iocoder.cn', 111, '2025-12-01 00:00:00', 100, '1', '2024-02-27 21:58:25', '1', '2024-02-28 22:53:54', b'0');
5478 -INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (154, 'hh', 129, 'hh', NULL, 0, 'http://hh.iocoder.cn', 111, '2024-04-30 00:00:00', 123, '1', '2024-03-30 17:52:59', '1', '2024-03-30 17:52:59', b'0'); 5482 +INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `website`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (154, 'hh', 129, 'hh', NULL, 0, 'http://hh.iocoder.cn', 111, '2024-04-30 00:00:00', 123, '1', '2024-03-30 17:52:59', '1', '2024-04-03 15:06:42', b'0');
5479 COMMIT; 5483 COMMIT;
5480 5484
5481 -- ---------------------------- 5485 -- ----------------------------
@@ -5619,7 +5623,7 @@ CREATE TABLE `system_users` ( @@ -5619,7 +5623,7 @@ CREATE TABLE `system_users` (
5619 -- Records of system_users 5623 -- Records of system_users
5620 -- ---------------------------- 5624 -- ----------------------------
5621 BEGIN; 5625 BEGIN;
5622 -INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/96c787a2ce88bf6d0ce3cd8b6cf5314e80e7703cd41bf4af8cd2e2909dbd6b6d.png', 0, '0:0:0:0:0:0:0:1', '2024-03-30 17:18:34', 'admin', '2021-01-05 17:03:47', NULL, '2024-03-30 17:18:34', b'0', 1); 5626 +INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '芋道源码', '管理员', 103, '[1]', 'aoteman@126.com', '18818260277', 2, 'http://test.yudao.iocoder.cn/96c787a2ce88bf6d0ce3cd8b6cf5314e80e7703cd41bf4af8cd2e2909dbd6b6d.png', 0, '127.0.0.1', '2024-04-03 17:31:00', 'admin', '2021-01-05 17:03:47', NULL, '2024-04-03 17:31:00', b'0', 1);
5623 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '127.0.0.1', '2022-07-09 23:03:33', '', '2021-01-07 09:07:17', NULL, '2022-07-09 23:03:33', b'0', 1); 5627 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$10$11U48RhyJ5pSBYWSn12AD./ld671.ycSzJHbyrtpeoMeYiw31eo8a', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, '', 1, '127.0.0.1', '2022-07-09 23:03:33', '', '2021-01-07 09:07:17', NULL, '2022-07-09 23:03:33', b'0', 1);
5624 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$10$YMpimV4T6BtDhIaA8jSW.u8UTGBeGhc/qwXP4oxoMr4mOw9.qttt6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '0:0:0:0:0:0:0:1', '2024-03-18 21:09:04', '', '2021-01-13 23:50:35', NULL, '2024-03-18 21:09:04', b'0', 1); 5628 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$10$YMpimV4T6BtDhIaA8jSW.u8UTGBeGhc/qwXP4oxoMr4mOw9.qttt6', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, '', 0, '0:0:0:0:0:0:0:1', '2024-03-18 21:09:04', '', '2021-01-13 23:50:35', NULL, '2024-03-18 21:09:04', b'0', 1);
5625 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$KhExCYl7lx6eWWZYKsibKOZ8IBJRyuNuCcEOLQ11RYhJKgHmlSwK.', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-03-26 07:11:35', '', '2021-01-21 02:13:53', NULL, '2024-03-26 07:11:35', b'0', 1); 5629 INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$KhExCYl7lx6eWWZYKsibKOZ8IBJRyuNuCcEOLQ11RYhJKgHmlSwK.', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '0:0:0:0:0:0:0:1', '2024-03-26 07:11:35', '', '2021-01-21 02:13:53', NULL, '2024-03-26 07:11:35', b'0', 1);
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringAopUtils.java deleted
1 -package cn.iocoder.yudao.framework.common.util.spring;  
2 -  
3 -import cn.hutool.core.bean.BeanUtil;  
4 -import org.springframework.aop.framework.AdvisedSupport;  
5 -import org.springframework.aop.framework.AopProxy;  
6 -import org.springframework.aop.support.AopUtils;  
7 -  
8 -/**  
9 - * Spring AOP 工具类  
10 - *  
11 - * 参考波克尔 http://www.bubuko.com/infodetail-3471885.html 实现  
12 - */  
13 -public class SpringAopUtils {  
14 -  
15 - /**  
16 - * 获取代理的目标对象  
17 - *  
18 - * @param proxy 代理对象  
19 - * @return 目标对象  
20 - */  
21 - public static Object getTarget(Object proxy) throws Exception {  
22 - // 不是代理对象  
23 - if (!AopUtils.isAopProxy(proxy)) {  
24 - return proxy;  
25 - }  
26 - // Jdk 代理  
27 - if (AopUtils.isJdkDynamicProxy(proxy)) {  
28 - return getJdkDynamicProxyTargetObject(proxy);  
29 - }  
30 - // Cglib 代理  
31 - return getCglibProxyTargetObject(proxy);  
32 - }  
33 -  
34 - private static Object getCglibProxyTargetObject(Object proxy) throws Exception {  
35 - Object dynamicAdvisedInterceptor = BeanUtil.getFieldValue(proxy, "CGLIB$CALLBACK_0");  
36 - AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(dynamicAdvisedInterceptor, "advised");  
37 - return advisedSupport.getTargetSource().getTarget();  
38 - }  
39 -  
40 - private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {  
41 - AopProxy aopProxy = (AopProxy) BeanUtil.getFieldValue(proxy, "h");  
42 - AdvisedSupport advisedSupport = (AdvisedSupport) BeanUtil.getFieldValue(aopProxy, "advised");  
43 - return advisedSupport.getTargetSource().getTarget();  
44 - }  
45 -  
46 -}  
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/spring/SpringUtils.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.common.util.spring;
  2 +
  3 +import cn.hutool.extra.spring.SpringUtil;
  4 +
  5 +import java.util.Objects;
  6 +
  7 +/**
  8 + * Spring 工具类
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +public class SpringUtils extends SpringUtil {
  13 +
  14 + /**
  15 + * 是否为生产环境
  16 + *
  17 + * @return 是否生产环境
  18 + */
  19 + public static boolean isProd() {
  20 + String activeProfile = getActiveProfile();
  21 + return Objects.equals("prod", activeProfile);
  22 + }
  23 +
  24 +}
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/config/YudaoApiLogAutoConfiguration.java
1 package cn.iocoder.yudao.framework.apilog.config; 1 package cn.iocoder.yudao.framework.apilog.config;
2 2
3 import cn.iocoder.yudao.framework.apilog.core.filter.ApiAccessLogFilter; 3 import cn.iocoder.yudao.framework.apilog.core.filter.ApiAccessLogFilter;
  4 +import cn.iocoder.yudao.framework.apilog.core.interceptor.ApiAccessLogInterceptor;
4 import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService; 5 import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService;
5 import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkServiceImpl; 6 import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkServiceImpl;
6 import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService; 7 import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
@@ -10,23 +11,26 @@ import cn.iocoder.yudao.framework.web.config.WebProperties; @@ -10,23 +11,26 @@ import cn.iocoder.yudao.framework.web.config.WebProperties;
10 import cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration; 11 import cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration;
11 import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi; 12 import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi;
12 import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi; 13 import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;
  14 +import jakarta.servlet.Filter;
13 import org.springframework.beans.factory.annotation.Value; 15 import org.springframework.beans.factory.annotation.Value;
14 import org.springframework.boot.autoconfigure.AutoConfiguration; 16 import org.springframework.boot.autoconfigure.AutoConfiguration;
15 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 17 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
16 import org.springframework.boot.web.servlet.FilterRegistrationBean; 18 import org.springframework.boot.web.servlet.FilterRegistrationBean;
17 import org.springframework.context.annotation.Bean; 19 import org.springframework.context.annotation.Bean;
18 -  
19 -import jakarta.servlet.Filter; 20 +import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  21 +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
20 22
21 @AutoConfiguration(after = YudaoWebAutoConfiguration.class) 23 @AutoConfiguration(after = YudaoWebAutoConfiguration.class)
22 -public class YudaoApiLogAutoConfiguration { 24 +public class YudaoApiLogAutoConfiguration implements WebMvcConfigurer {
23 25
24 @Bean 26 @Bean
  27 + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
25 public ApiAccessLogFrameworkService apiAccessLogFrameworkService(ApiAccessLogApi apiAccessLogApi) { 28 public ApiAccessLogFrameworkService apiAccessLogFrameworkService(ApiAccessLogApi apiAccessLogApi) {
26 return new ApiAccessLogFrameworkServiceImpl(apiAccessLogApi); 29 return new ApiAccessLogFrameworkServiceImpl(apiAccessLogApi);
27 } 30 }
28 31
29 @Bean 32 @Bean
  33 + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
30 public ApiErrorLogFrameworkService apiErrorLogFrameworkService(ApiErrorLogApi apiErrorLogApi) { 34 public ApiErrorLogFrameworkService apiErrorLogFrameworkService(ApiErrorLogApi apiErrorLogApi) {
31 return new ApiErrorLogFrameworkServiceImpl(apiErrorLogApi); 35 return new ApiErrorLogFrameworkServiceImpl(apiErrorLogApi);
32 } 36 }
@@ -49,4 +53,9 @@ public class YudaoApiLogAutoConfiguration { @@ -49,4 +53,9 @@ public class YudaoApiLogAutoConfiguration {
49 return bean; 53 return bean;
50 } 54 }
51 55
  56 + @Override
  57 + public void addInterceptors(InterceptorRegistry registry) {
  58 + registry.addInterceptor(new ApiAccessLogInterceptor());
  59 + }
  60 +
52 } 61 }
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/annotations/ApiAccessLog.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.apilog.core.annotations;
  2 +
  3 +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum;
  4 +
  5 +import java.lang.annotation.ElementType;
  6 +import java.lang.annotation.Retention;
  7 +import java.lang.annotation.RetentionPolicy;
  8 +import java.lang.annotation.Target;
  9 +
  10 +/**
  11 + * 访问日志注解
  12 + *
  13 + * @author 芋道源码
  14 + */
  15 +@Target({ElementType.METHOD})
  16 +@Retention(RetentionPolicy.RUNTIME)
  17 +public @interface ApiAccessLog {
  18 +
  19 + // ========== 开关字段 ==========
  20 +
  21 + /**
  22 + * 是否记录访问日志
  23 + */
  24 + boolean enable() default true;
  25 + /**
  26 + * 是否记录请求参数
  27 + *
  28 + * 默认记录,主要考虑请求数据一般不大。可手动设置为 false 进行关闭
  29 + */
  30 + boolean requestEnable() default true;
  31 + /**
  32 + * 是否记录响应结果
  33 + *
  34 + * 默认不记录,主要考虑响应数据可能比较大。可手动设置为 true 进行打开
  35 + */
  36 + boolean responseEnable() default false;
  37 + /**
  38 + * 敏感参数数组
  39 + *
  40 + * 添加后,请求参数、响应结果不会记录该参数
  41 + */
  42 + String[] sanitizeKeys() default {};
  43 +
  44 + // ========== 模块字段 ==========
  45 +
  46 + /**
  47 + * 操作模块
  48 + *
  49 + * 为空时,会尝试读取 {@link io.swagger.v3.oas.annotations.tags.Tag#name()} 属性
  50 + */
  51 + String operateModule() default "";
  52 + /**
  53 + * 操作名
  54 + *
  55 + * 为空时,会尝试读取 {@link io.swagger.v3.oas.annotations.Operation#summary()} 属性
  56 + */
  57 + String operateName() default "";
  58 + /**
  59 + * 操作分类
  60 + *
  61 + * 实际并不是数组,因为枚举不能设置 null 作为默认值
  62 + */
  63 + OperateTypeEnum[] operateType() default {};
  64 +
  65 +}
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/enums/OperateTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.apilog.core.enums;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 操作日志的操作类型
  8 + *
  9 + * @author ruoyi
  10 + */
  11 +@Getter
  12 +@AllArgsConstructor
  13 +public enum OperateTypeEnum {
  14 +
  15 + /**
  16 + * 查询
  17 + */
  18 + GET(1),
  19 + /**
  20 + * 新增
  21 + */
  22 + CREATE(2),
  23 + /**
  24 + * 修改
  25 + */
  26 + UPDATE(3),
  27 + /**
  28 + * 删除
  29 + */
  30 + DELETE(4),
  31 + /**
  32 + * 导出
  33 + */
  34 + EXPORT(5),
  35 + /**
  36 + * 导入
  37 + */
  38 + IMPORT(6),
  39 + /**
  40 + * 其它
  41 + *
  42 + * 在无法归类时,可以选择使用其它。因为还有操作名可以进一步标识
  43 + */
  44 + OTHER(0);
  45 +
  46 + /**
  47 + * 类型
  48 + */
  49 + private final Integer type;
  50 +
  51 +}
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java
1 package cn.iocoder.yudao.framework.apilog.core.filter; 1 package cn.iocoder.yudao.framework.apilog.core.filter;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.date.LocalDateTimeUtil; 4 import cn.hutool.core.date.LocalDateTimeUtil;
4 import cn.hutool.core.exceptions.ExceptionUtil; 5 import cn.hutool.core.exceptions.ExceptionUtil;
5 import cn.hutool.core.map.MapUtil; 6 import cn.hutool.core.map.MapUtil;
6 -import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLog; 7 +import cn.hutool.core.util.ArrayUtil;
  8 +import cn.hutool.core.util.BooleanUtil;
  9 +import cn.hutool.core.util.StrUtil;
  10 +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog;
  11 +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum;
7 import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService; 12 import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService;
8 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; 13 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
9 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 14 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  15 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
10 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; 16 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
11 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; 17 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
12 import cn.iocoder.yudao.framework.web.config.WebProperties; 18 import cn.iocoder.yudao.framework.web.config.WebProperties;
13 import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter; 19 import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter;
14 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 20 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
15 -import lombok.extern.slf4j.Slf4j;  
16 - 21 +import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
  22 +import com.fasterxml.jackson.databind.JsonNode;
  23 +import io.swagger.v3.oas.annotations.Operation;
  24 +import io.swagger.v3.oas.annotations.tags.Tag;
17 import jakarta.servlet.FilterChain; 25 import jakarta.servlet.FilterChain;
18 import jakarta.servlet.ServletException; 26 import jakarta.servlet.ServletException;
19 import jakarta.servlet.http.HttpServletRequest; 27 import jakarta.servlet.http.HttpServletRequest;
20 import jakarta.servlet.http.HttpServletResponse; 28 import jakarta.servlet.http.HttpServletResponse;
  29 +import lombok.extern.slf4j.Slf4j;
  30 +import org.springframework.web.bind.annotation.RequestMethod;
  31 +import org.springframework.web.method.HandlerMethod;
  32 +
21 import java.io.IOException; 33 import java.io.IOException;
22 import java.time.LocalDateTime; 34 import java.time.LocalDateTime;
23 import java.time.temporal.ChronoUnit; 35 import java.time.temporal.ChronoUnit;
  36 +import java.util.Iterator;
24 import java.util.Map; 37 import java.util.Map;
25 38
  39 +import static cn.iocoder.yudao.framework.apilog.core.interceptor.ApiAccessLogInterceptor.*;
26 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 40 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
27 41
28 /** 42 /**
29 * API 访问日志 Filter 43 * API 访问日志 Filter
30 * 44 *
  45 + * 目的:记录 API 访问日志到数据库中
  46 + *
31 * @author 芋道源码 47 * @author 芋道源码
32 */ 48 */
33 @Slf4j 49 @Slf4j
34 public class ApiAccessLogFilter extends ApiRequestFilter { 50 public class ApiAccessLogFilter extends ApiRequestFilter {
35 51
  52 + private static final String[] SANITIZE_KEYS = new String[]{"password", "token", "accessToken", "refreshToken"};
  53 +
36 private final String applicationName; 54 private final String applicationName;
37 55
38 private final ApiAccessLogFrameworkService apiAccessLogFrameworkService; 56 private final ApiAccessLogFrameworkService apiAccessLogFrameworkService;
@@ -44,6 +62,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter { @@ -44,6 +62,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
44 } 62 }
45 63
46 @Override 64 @Override
  65 + @SuppressWarnings("NullableProblems")
47 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 66 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
48 throws ServletException, IOException { 67 throws ServletException, IOException {
49 // 获得开始时间 68 // 获得开始时间
@@ -66,45 +85,166 @@ public class ApiAccessLogFilter extends ApiRequestFilter { @@ -66,45 +85,166 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
66 85
67 private void createApiAccessLog(HttpServletRequest request, LocalDateTime beginTime, 86 private void createApiAccessLog(HttpServletRequest request, LocalDateTime beginTime,
68 Map<String, String> queryString, String requestBody, Exception ex) { 87 Map<String, String> queryString, String requestBody, Exception ex) {
69 - ApiAccessLog accessLog = new ApiAccessLog(); 88 + ApiAccessLogCreateReqDTO accessLog = new ApiAccessLogCreateReqDTO();
70 try { 89 try {
71 - this.buildApiAccessLogDTO(accessLog, request, beginTime, queryString, requestBody, ex); 90 + boolean enable = buildApiAccessLog(accessLog, request, beginTime, queryString, requestBody, ex);
  91 + if (!enable) {
  92 + return;
  93 + }
72 apiAccessLogFrameworkService.createApiAccessLog(accessLog); 94 apiAccessLogFrameworkService.createApiAccessLog(accessLog);
73 } catch (Throwable th) { 95 } catch (Throwable th) {
74 log.error("[createApiAccessLog][url({}) log({}) 发生异常]", request.getRequestURI(), toJsonString(accessLog), th); 96 log.error("[createApiAccessLog][url({}) log({}) 发生异常]", request.getRequestURI(), toJsonString(accessLog), th);
75 } 97 }
76 } 98 }
77 99
78 - private void buildApiAccessLogDTO(ApiAccessLog accessLog, HttpServletRequest request, LocalDateTime beginTime, 100 + private boolean buildApiAccessLog(ApiAccessLogCreateReqDTO accessLog, HttpServletRequest request, LocalDateTime beginTime,
79 Map<String, String> queryString, String requestBody, Exception ex) { 101 Map<String, String> queryString, String requestBody, Exception ex) {
  102 + // 判断:是否要记录操作日志
  103 + HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(ATTRIBUTE_HANDLER_METHOD);
  104 + ApiAccessLog accessLogAnnotation = null;
  105 + if (handlerMethod != null) {
  106 + accessLogAnnotation = handlerMethod.getMethodAnnotation(ApiAccessLog.class);
  107 + if (accessLogAnnotation != null && BooleanUtil.isFalse(accessLogAnnotation.enable())) {
  108 + return false;
  109 + }
  110 + }
  111 +
80 // 处理用户信息 112 // 处理用户信息
81 - accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request));  
82 - accessLog.setUserType(WebFrameworkUtils.getLoginUserType(request)); 113 + accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request))
  114 + .setUserType(WebFrameworkUtils.getLoginUserType(request));
83 // 设置访问结果 115 // 设置访问结果
84 CommonResult<?> result = WebFrameworkUtils.getCommonResult(request); 116 CommonResult<?> result = WebFrameworkUtils.getCommonResult(request);
85 if (result != null) { 117 if (result != null) {
86 - accessLog.setResultCode(result.getCode());  
87 - accessLog.setResultMsg(result.getMsg()); 118 + accessLog.setResultCode(result.getCode()).setResultMsg(result.getMsg());
88 } else if (ex != null) { 119 } else if (ex != null) {
89 - accessLog.setResultCode(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode());  
90 - accessLog.setResultMsg(ExceptionUtil.getRootCauseMessage(ex)); 120 + accessLog.setResultCode(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode())
  121 + .setResultMsg(ExceptionUtil.getRootCauseMessage(ex));
91 } else { 122 } else {
92 - accessLog.setResultCode(0);  
93 - accessLog.setResultMsg("");  
94 - }  
95 - // 设置其它字段  
96 - accessLog.setTraceId(TracerUtils.getTraceId());  
97 - accessLog.setApplicationName(applicationName);  
98 - accessLog.setRequestUrl(request.getRequestURI());  
99 - Map<String, Object> requestParams = MapUtil.<String, Object>builder().put("query", queryString).put("body", requestBody).build();  
100 - accessLog.setRequestParams(toJsonString(requestParams));  
101 - accessLog.setRequestMethod(request.getMethod());  
102 - accessLog.setUserAgent(ServletUtils.getUserAgent(request));  
103 - accessLog.setUserIp(ServletUtils.getClientIP(request)); 123 + accessLog.setResultCode(GlobalErrorCodeConstants.SUCCESS.getCode()).setResultMsg("");
  124 + }
  125 + // 设置请求字段
  126 + accessLog.setTraceId(TracerUtils.getTraceId()).setApplicationName(applicationName)
  127 + .setRequestUrl(request.getRequestURI()).setRequestMethod(request.getMethod())
  128 + .setUserAgent(ServletUtils.getUserAgent(request)).setUserIp(ServletUtils.getClientIP(request));
  129 + String[] sanitizeKeys = accessLogAnnotation != null ? accessLogAnnotation.sanitizeKeys() : null;
  130 + Boolean requestEnable = accessLogAnnotation != null ? accessLogAnnotation.requestEnable() : Boolean.TRUE;
  131 + if (!BooleanUtil.isFalse(requestEnable)) { // 默认记录,所以判断 !false
  132 + Map<String, Object> requestParams = MapUtil.<String, Object>builder()
  133 + .put("query", sanitizeMap(queryString, sanitizeKeys))
  134 + .put("body", sanitizeJson(requestBody, sanitizeKeys)).build();
  135 + accessLog.setRequestParams(toJsonString(requestParams));
  136 + }
  137 + Boolean responseEnable = accessLogAnnotation != null ? accessLogAnnotation.responseEnable() : Boolean.FALSE;
  138 + if (BooleanUtil.isTrue(responseEnable)) { // 默认不记录,默认强制要求 true
  139 + accessLog.setResponseBody(sanitizeJson(result, sanitizeKeys));
  140 + }
104 // 持续时间 141 // 持续时间
105 - accessLog.setBeginTime(beginTime);  
106 - accessLog.setEndTime(LocalDateTime.now());  
107 - accessLog.setDuration((int) LocalDateTimeUtil.between(accessLog.getBeginTime(), accessLog.getEndTime(), ChronoUnit.MILLIS)); 142 + accessLog.setBeginTime(beginTime).setEndTime(LocalDateTime.now())
  143 + .setDuration((int) LocalDateTimeUtil.between(accessLog.getBeginTime(), accessLog.getEndTime(), ChronoUnit.MILLIS));
  144 +
  145 + // 操作模块
  146 + if (handlerMethod != null) {
  147 + Tag tagAnnotation = handlerMethod.getBeanType().getAnnotation(Tag.class);
  148 + Operation operationAnnotation = handlerMethod.getMethodAnnotation(Operation.class);
  149 + String operateModule = accessLogAnnotation != null ? accessLogAnnotation.operateModule() :
  150 + tagAnnotation != null ? StrUtil.nullToDefault(tagAnnotation.name(), tagAnnotation.description()) : null;
  151 + String operateName = accessLogAnnotation != null ? accessLogAnnotation.operateName() :
  152 + operationAnnotation != null ? operationAnnotation.summary() : null;
  153 + OperateTypeEnum operateType = accessLogAnnotation != null && accessLogAnnotation.operateType().length > 0 ?
  154 + accessLogAnnotation.operateType()[0] : parseOperateLogType(request);
  155 + accessLog.setOperateModule(operateModule).setOperateName(operateName).setOperateType(operateType.getType());
  156 + }
  157 + return true;
  158 + }
  159 +
  160 + // ========== 解析 @ApiAccessLog、@Swagger 注解 ==========
  161 +
  162 + private static OperateTypeEnum parseOperateLogType(HttpServletRequest request) {
  163 + RequestMethod requestMethod = RequestMethod.resolve(request.getMethod());
  164 + if (requestMethod == null) {
  165 + return OperateTypeEnum.OTHER;
  166 + }
  167 + switch (requestMethod) {
  168 + case GET:
  169 + return OperateTypeEnum.GET;
  170 + case POST:
  171 + return OperateTypeEnum.CREATE;
  172 + case PUT:
  173 + return OperateTypeEnum.UPDATE;
  174 + case DELETE:
  175 + return OperateTypeEnum.DELETE;
  176 + default:
  177 + return OperateTypeEnum.OTHER;
  178 + }
  179 + }
  180 +
  181 + // ========== 请求和响应的脱敏逻辑,移除类似 password、token 等敏感字段 ==========
  182 +
  183 + private static String sanitizeMap(Map<String, ?> map, String[] sanitizeKeys) {
  184 + if (CollUtil.isNotEmpty(map)) {
  185 + return null;
  186 + }
  187 + if (sanitizeKeys != null) {
  188 + MapUtil.removeAny(map, sanitizeKeys);
  189 + }
  190 + MapUtil.removeAny(map, SANITIZE_KEYS);
  191 + return JsonUtils.toJsonString(map);
  192 + }
  193 +
  194 + private static String sanitizeJson(String jsonString, String[] sanitizeKeys) {
  195 + if (StrUtil.isEmpty(jsonString)) {
  196 + return null;
  197 + }
  198 + try {
  199 + JsonNode rootNode = JsonUtils.parseTree(jsonString);
  200 + sanitizeJson(rootNode, sanitizeKeys);
  201 + return JsonUtils.toJsonString(rootNode);
  202 + } catch (Exception e) {
  203 + // 脱敏失败的情况下,直接忽略异常,避免影响用户请求
  204 + log.error("[sanitizeJson][脱敏({}) 发生异常]", jsonString, e);
  205 + return jsonString;
  206 + }
  207 + }
  208 +
  209 + private static String sanitizeJson(CommonResult<?> commonResult, String[] sanitizeKeys) {
  210 + if (commonResult == null) {
  211 + return null;
  212 + }
  213 + String jsonString = toJsonString(commonResult);
  214 + try {
  215 + JsonNode rootNode = JsonUtils.parseTree(jsonString);
  216 + sanitizeJson(rootNode.get("data"), sanitizeKeys); // 只处理 data 字段,不处理 code、msg 字段,避免错误被脱敏掉
  217 + return JsonUtils.toJsonString(rootNode);
  218 + } catch (Exception e) {
  219 + // 脱敏失败的情况下,直接忽略异常,避免影响用户请求
  220 + log.error("[sanitizeJson][脱敏({}) 发生异常]", jsonString, e);
  221 + return jsonString;
  222 + }
  223 + }
  224 +
  225 + private static void sanitizeJson(JsonNode node, String[] sanitizeKeys) {
  226 + // 情况一:数组,遍历处理
  227 + if (node.isArray()) {
  228 + for (JsonNode childNode : node) {
  229 + sanitizeJson(childNode, sanitizeKeys);
  230 + }
  231 + return;
  232 + }
  233 + // 情况二:非 Object,只是某个值,直接返回
  234 + if (!node.isObject()) {
  235 + return;
  236 + }
  237 + // 情况三:Object,遍历处理
  238 + Iterator<Map.Entry<String, JsonNode>> iterator = node.properties().iterator();
  239 + while (iterator.hasNext()) {
  240 + Map.Entry<String, JsonNode> entry = iterator.next();
  241 + if (ArrayUtil.contains(sanitizeKeys, entry.getKey())
  242 + || ArrayUtil.contains(SANITIZE_KEYS, entry.getKey())) {
  243 + iterator.remove();
  244 + continue;
  245 + }
  246 + sanitizeJson(entry.getValue(), sanitizeKeys);
  247 + }
108 } 248 }
109 249
110 } 250 }
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.apilog.core.interceptor;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
  6 +import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
  7 +import jakarta.servlet.http.HttpServletRequest;
  8 +import jakarta.servlet.http.HttpServletResponse;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.springframework.util.StopWatch;
  11 +import org.springframework.web.method.HandlerMethod;
  12 +import org.springframework.web.servlet.HandlerInterceptor;
  13 +
  14 +import java.util.Map;
  15 +
  16 +/**
  17 + * API 访问日志 Interceptor
  18 + *
  19 + * 目的:在非 prod 环境时,打印 request 和 response 两条日志到日志文件(控制台)中。
  20 + *
  21 + * @author 芋道源码
  22 + */
  23 +@Slf4j
  24 +public class ApiAccessLogInterceptor implements HandlerInterceptor {
  25 +
  26 + public static String ATTRIBUTE_HANDLER_METHOD = "HANDLER_METHOD";
  27 +
  28 + private static String ATTRIBUTE_STOP_WATCH = "ApiAccessLogInterceptor.StopWatch";
  29 +
  30 + @Override
  31 + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
  32 + // 记录 HandlerMethod,提供给 ApiAccessLogFilter 使用
  33 + HandlerMethod handlerMethod = handler instanceof HandlerMethod ? (HandlerMethod) handler : null;
  34 + if (handlerMethod != null) {
  35 + request.setAttribute(ATTRIBUTE_HANDLER_METHOD, handlerMethod);
  36 + }
  37 +
  38 + // 打印 request 日志
  39 + if (!SpringUtils.isProd()) {
  40 + Map<String, String> queryString = ServletUtils.getParamMap(request);
  41 + String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null;
  42 + if (CollUtil.isEmpty(queryString) && StrUtil.isEmpty(requestBody)) {
  43 + log.info("[preHandle][开始请求 URL({}) 无参数]", request.getRequestURI());
  44 + } else {
  45 + log.info("[preHandle][开始请求 URL({}) 参数({})]", request.getRequestURI(),
  46 + StrUtil.nullToDefault(requestBody, queryString.toString()));
  47 + }
  48 + // 计时
  49 + StopWatch stopWatch = new StopWatch();
  50 + stopWatch.start();
  51 + request.setAttribute(ATTRIBUTE_STOP_WATCH, stopWatch);
  52 + }
  53 + return true;
  54 + }
  55 +
  56 + @Override
  57 + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
  58 + // 打印 response 日志
  59 + if (!SpringUtils.isProd()) {
  60 + StopWatch stopWatch = (StopWatch) request.getAttribute(ATTRIBUTE_STOP_WATCH);
  61 + stopWatch.stop();
  62 + log.info("[afterCompletion][完成请求 URL({}) 耗时({} ms)]",
  63 + request.getRequestURI(), stopWatch.getTotalTimeMillis());
  64 + }
  65 + }
  66 +
  67 +}
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java deleted
1 -package cn.iocoder.yudao.framework.apilog.core.service;  
2 -  
3 -import lombok.Data;  
4 -  
5 -import jakarta.validation.constraints.NotNull;  
6 -import java.time.LocalDateTime;  
7 -  
8 -/**  
9 - * API 访问日志  
10 - *  
11 - * @author 芋道源码  
12 - */  
13 -@Data  
14 -public class ApiAccessLog {  
15 -  
16 - /**  
17 - * 链路追踪编号  
18 - */  
19 - private String traceId;  
20 - /**  
21 - * 用户编号  
22 - */  
23 - private Long userId;  
24 - /**  
25 - * 用户类型  
26 - */  
27 - private Integer userType;  
28 - /**  
29 - * 应用名  
30 - */  
31 - @NotNull(message = "应用名不能为空")  
32 - private String applicationName;  
33 -  
34 - /**  
35 - * 请求方法名  
36 - */  
37 - @NotNull(message = "http 请求方法不能为空")  
38 - private String requestMethod;  
39 - /**  
40 - * 访问地址  
41 - */  
42 - @NotNull(message = "访问地址不能为空")  
43 - private String requestUrl;  
44 - /**  
45 - * 请求参数  
46 - */  
47 - @NotNull(message = "请求参数不能为空")  
48 - private String requestParams;  
49 - /**  
50 - * 用户 IP  
51 - */  
52 - @NotNull(message = "ip 不能为空")  
53 - private String userIp;  
54 - /**  
55 - * 浏览器 UA  
56 - */  
57 - @NotNull(message = "User-Agent 不能为空")  
58 - private String userAgent;  
59 -  
60 - /**  
61 - * 开始请求时间  
62 - */  
63 - @NotNull(message = "开始请求时间不能为空")  
64 - private LocalDateTime beginTime;  
65 - /**  
66 - * 结束请求时间  
67 - */  
68 - @NotNull(message = "结束请求时间不能为空")  
69 - private LocalDateTime endTime;  
70 - /**  
71 - * 执行时长,单位:毫秒  
72 - */  
73 - @NotNull(message = "执行时长不能为空")  
74 - private Integer duration;  
75 - /**  
76 - * 结果码  
77 - */  
78 - @NotNull(message = "错误码不能为空")  
79 - private Integer resultCode;  
80 - /**  
81 - * 结果提示  
82 - */  
83 - private String resultMsg;  
84 -  
85 -}  
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkService.java
1 package cn.iocoder.yudao.framework.apilog.core.service; 1 package cn.iocoder.yudao.framework.apilog.core.service;
2 2
  3 +import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
  4 +
3 /** 5 /**
4 * API 访问日志 Framework Service 接口 6 * API 访问日志 Framework Service 接口
5 * 7 *
@@ -10,7 +12,8 @@ public interface ApiAccessLogFrameworkService { @@ -10,7 +12,8 @@ public interface ApiAccessLogFrameworkService {
10 /** 12 /**
11 * 创建 API 访问日志 13 * 创建 API 访问日志
12 * 14 *
13 - * @param apiAccessLog API 访问日志 15 + * @param reqDTO API 访问日志
14 */ 16 */
15 - void createApiAccessLog(ApiAccessLog apiAccessLog); 17 + void createApiAccessLog(ApiAccessLogCreateReqDTO reqDTO);
  18 +
16 } 19 }
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLogFrameworkServiceImpl.java
1 package cn.iocoder.yudao.framework.apilog.core.service; 1 package cn.iocoder.yudao.framework.apilog.core.service;
2 2
3 -import cn.hutool.core.bean.BeanUtil;  
4 import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi; 3 import cn.iocoder.yudao.module.infra.api.logger.ApiAccessLogApi;
5 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; 4 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
6 import lombok.RequiredArgsConstructor; 5 import lombok.RequiredArgsConstructor;
@@ -20,8 +19,7 @@ public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkSe @@ -20,8 +19,7 @@ public class ApiAccessLogFrameworkServiceImpl implements ApiAccessLogFrameworkSe
20 19
21 @Override 20 @Override
22 @Async 21 @Async
23 - public void createApiAccessLog(ApiAccessLog apiAccessLog) {  
24 - ApiAccessLogCreateReqDTO reqDTO = BeanUtil.copyProperties(apiAccessLog, ApiAccessLogCreateReqDTO.class); 22 + public void createApiAccessLog(ApiAccessLogCreateReqDTO reqDTO) {
25 apiAccessLogApi.createApiAccessLog(reqDTO); 23 apiAccessLogApi.createApiAccessLog(reqDTO);
26 } 24 }
27 25
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java deleted
1 -package cn.iocoder.yudao.framework.apilog.core.service;  
2 -  
3 -import lombok.Data;  
4 -  
5 -import jakarta.validation.constraints.NotNull;  
6 -import java.time.LocalDateTime;  
7 -  
8 -/**  
9 - * API 错误日志  
10 - *  
11 - * @author 芋道源码  
12 - */  
13 -@Data  
14 -public class ApiErrorLog {  
15 -  
16 - /**  
17 - * 链路编号  
18 - */  
19 - private String traceId;  
20 - /**  
21 - * 账号编号  
22 - */  
23 - private Long userId;  
24 - /**  
25 - * 用户类型  
26 - */  
27 - private Integer userType;  
28 - /**  
29 - * 应用名  
30 - */  
31 - @NotNull(message = "应用名不能为空")  
32 - private String applicationName;  
33 -  
34 - /**  
35 - * 请求方法名  
36 - */  
37 - @NotNull(message = "http 请求方法不能为空")  
38 - private String requestMethod;  
39 - /**  
40 - * 访问地址  
41 - */  
42 - @NotNull(message = "访问地址不能为空")  
43 - private String requestUrl;  
44 - /**  
45 - * 请求参数  
46 - */  
47 - @NotNull(message = "请求参数不能为空")  
48 - private String requestParams;  
49 - /**  
50 - * 用户 IP  
51 - */  
52 - @NotNull(message = "ip 不能为空")  
53 - private String userIp;  
54 - /**  
55 - * 浏览器 UA  
56 - */  
57 - @NotNull(message = "User-Agent 不能为空")  
58 - private String userAgent;  
59 -  
60 - /**  
61 - * 异常时间  
62 - */  
63 - @NotNull(message = "异常时间不能为空")  
64 - private LocalDateTime exceptionTime;  
65 - /**  
66 - * 异常名  
67 - */  
68 - @NotNull(message = "异常名不能为空")  
69 - private String exceptionName;  
70 - /**  
71 - * 异常发生的类全名  
72 - */  
73 - @NotNull(message = "异常发生的类全名不能为空")  
74 - private String exceptionClassName;  
75 - /**  
76 - * 异常发生的类文件  
77 - */  
78 - @NotNull(message = "异常发生的类文件不能为空")  
79 - private String exceptionFileName;  
80 - /**  
81 - * 异常发生的方法名  
82 - */  
83 - @NotNull(message = "异常发生的方法名不能为空")  
84 - private String exceptionMethodName;  
85 - /**  
86 - * 异常发生的方法所在行  
87 - */  
88 - @NotNull(message = "异常发生的方法所在行不能为空")  
89 - private Integer exceptionLineNumber;  
90 - /**  
91 - * 异常的栈轨迹异常的栈轨迹  
92 - */  
93 - @NotNull(message = "异常的栈轨迹不能为空")  
94 - private String exceptionStackTrace;  
95 - /**  
96 - * 异常导致的根消息  
97 - */  
98 - @NotNull(message = "异常导致的根消息不能为空")  
99 - private String exceptionRootCauseMessage;  
100 - /**  
101 - * 异常导致的消息  
102 - */  
103 - @NotNull(message = "异常导致的消息不能为空")  
104 - private String exceptionMessage;  
105 -  
106 -  
107 -}  
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkService.java
1 package cn.iocoder.yudao.framework.apilog.core.service; 1 package cn.iocoder.yudao.framework.apilog.core.service;
2 2
  3 +import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
  4 +
3 /** 5 /**
4 * API 错误日志 Framework Service 接口 6 * API 错误日志 Framework Service 接口
5 * 7 *
@@ -10,7 +12,8 @@ public interface ApiErrorLogFrameworkService { @@ -10,7 +12,8 @@ public interface ApiErrorLogFrameworkService {
10 /** 12 /**
11 * 创建 API 错误日志 13 * 创建 API 错误日志
12 * 14 *
13 - * @param apiErrorLog API 错误日志 15 + * @param reqDTO API 错误日志
14 */ 16 */
15 - void createApiErrorLog(ApiErrorLog apiErrorLog); 17 + void createApiErrorLog(ApiErrorLogCreateReqDTO reqDTO);
  18 +
16 } 19 }
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLogFrameworkServiceImpl.java
1 package cn.iocoder.yudao.framework.apilog.core.service; 1 package cn.iocoder.yudao.framework.apilog.core.service;
2 2
3 -import cn.hutool.core.bean.BeanUtil;  
4 import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi; 3 import cn.iocoder.yudao.module.infra.api.logger.ApiErrorLogApi;
5 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; 4 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
6 import lombok.RequiredArgsConstructor; 5 import lombok.RequiredArgsConstructor;
@@ -20,8 +19,7 @@ public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkServ @@ -20,8 +19,7 @@ public class ApiErrorLogFrameworkServiceImpl implements ApiErrorLogFrameworkServ
20 19
21 @Override 20 @Override
22 @Async 21 @Async
23 - public void createApiErrorLog(ApiErrorLog apiErrorLog) {  
24 - ApiErrorLogCreateReqDTO reqDTO = BeanUtil.copyProperties(apiErrorLog, ApiErrorLogCreateReqDTO.class); 22 + public void createApiErrorLog(ApiErrorLogCreateReqDTO reqDTO) {
25 apiErrorLogApi.createApiErrorLog(reqDTO); 23 apiErrorLogApi.createApiErrorLog(reqDTO);
26 } 24 }
27 25
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.framework.web.core.handler; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.framework.web.core.handler;
3 import cn.hutool.core.exceptions.ExceptionUtil; 3 import cn.hutool.core.exceptions.ExceptionUtil;
4 import cn.hutool.core.map.MapUtil; 4 import cn.hutool.core.map.MapUtil;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 -import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLog;  
7 import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService; 6 import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
8 import cn.iocoder.yudao.framework.common.exception.ServiceException; 7 import cn.iocoder.yudao.framework.common.exception.ServiceException;
9 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 8 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
@@ -11,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -11,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
11 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; 10 import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
12 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; 11 import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
13 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; 12 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
  13 +import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
14 import lombok.AllArgsConstructor; 14 import lombok.AllArgsConstructor;
15 import lombok.extern.slf4j.Slf4j; 15 import lombok.extern.slf4j.Slf4j;
16 import org.apache.commons.lang3.exception.ExceptionUtils; 16 import org.apache.commons.lang3.exception.ExceptionUtils;
@@ -46,6 +46,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC @@ -46,6 +46,7 @@ import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeC
46 @Slf4j 46 @Slf4j
47 public class GlobalExceptionHandler { 47 public class GlobalExceptionHandler {
48 48
  49 + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
49 private final String applicationName; 50 private final String applicationName;
50 51
51 private final ApiErrorLogFrameworkService apiErrorLogFrameworkService; 52 private final ApiErrorLogFrameworkService apiErrorLogFrameworkService;
@@ -237,10 +238,10 @@ public class GlobalExceptionHandler { @@ -237,10 +238,10 @@ public class GlobalExceptionHandler {
237 238
238 private void createExceptionLog(HttpServletRequest req, Throwable e) { 239 private void createExceptionLog(HttpServletRequest req, Throwable e) {
239 // 插入错误日志 240 // 插入错误日志
240 - ApiErrorLog errorLog = new ApiErrorLog(); 241 + ApiErrorLogCreateReqDTO errorLog = new ApiErrorLogCreateReqDTO();
241 try { 242 try {
242 // 初始化 errorLog 243 // 初始化 errorLog
243 - initExceptionLog(errorLog, req, e); 244 + buildExceptionLog(errorLog, req, e);
244 // 执行插入 errorLog 245 // 执行插入 errorLog
245 apiErrorLogFrameworkService.createApiErrorLog(errorLog); 246 apiErrorLogFrameworkService.createApiErrorLog(errorLog);
246 } catch (Throwable th) { 247 } catch (Throwable th) {
@@ -248,7 +249,7 @@ public class GlobalExceptionHandler { @@ -248,7 +249,7 @@ public class GlobalExceptionHandler {
248 } 249 }
249 } 250 }
250 251
251 - private void initExceptionLog(ApiErrorLog errorLog, HttpServletRequest request, Throwable e) { 252 + private void buildExceptionLog(ApiErrorLogCreateReqDTO errorLog, HttpServletRequest request, Throwable e) {
252 // 处理用户信息 253 // 处理用户信息
253 errorLog.setUserId(WebFrameworkUtils.getLoginUserId(request)); 254 errorLog.setUserId(WebFrameworkUtils.getLoginUserId(request));
254 errorLog.setUserType(WebFrameworkUtils.getLoginUserType(request)); 255 errorLog.setUserType(WebFrameworkUtils.getLoginUserType(request));
yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiAccessLogCreateReqDTO.java
@@ -44,9 +44,12 @@ public class ApiAccessLogCreateReqDTO { @@ -44,9 +44,12 @@ public class ApiAccessLogCreateReqDTO {
44 /** 44 /**
45 * 请求参数 45 * 请求参数
46 */ 46 */
47 - @NotNull(message = "请求参数不能为空")  
48 private String requestParams; 47 private String requestParams;
49 /** 48 /**
  49 + * 响应结果
  50 + */
  51 + private String responseBody;
  52 + /**
50 * 用户 IP 53 * 用户 IP
51 */ 54 */
52 @NotNull(message = "ip 不能为空") 55 @NotNull(message = "ip 不能为空")
@@ -58,6 +61,21 @@ public class ApiAccessLogCreateReqDTO { @@ -58,6 +61,21 @@ public class ApiAccessLogCreateReqDTO {
58 private String userAgent; 61 private String userAgent;
59 62
60 /** 63 /**
  64 + * 操作模块
  65 + */
  66 + private String operateModule;
  67 + /**
  68 + * 操作名
  69 + */
  70 + private String operateName;
  71 + /**
  72 + * 操作分类
  73 + *
  74 + * 枚举,参见 OperateTypeEnum 类
  75 + */
  76 + private Integer operateType;
  77 +
  78 + /**
61 * 开始请求时间 79 * 开始请求时间
62 */ 80 */
63 @NotNull(message = "开始请求时间不能为空") 81 @NotNull(message = "开始请求时间不能为空")
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java
@@ -48,6 +48,10 @@ public class ApiAccessLogRespVO { @@ -48,6 +48,10 @@ public class ApiAccessLogRespVO {
48 @ExcelProperty("请求参数") 48 @ExcelProperty("请求参数")
49 private String requestParams; 49 private String requestParams;
50 50
  51 + @Schema(description = "响应结果")
  52 + @ExcelProperty("响应结果")
  53 + private String responseBody;
  54 +
51 @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1") 55 @Schema(description = "用户 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
52 @ExcelProperty("用户 IP") 56 @ExcelProperty("用户 IP")
53 private String userIp; 57 private String userIp;
@@ -56,6 +60,19 @@ public class ApiAccessLogRespVO { @@ -56,6 +60,19 @@ public class ApiAccessLogRespVO {
56 @ExcelProperty("浏览器 UA") 60 @ExcelProperty("浏览器 UA")
57 private String userAgent; 61 private String userAgent;
58 62
  63 + @Schema(description = "操作模块", requiredMode = Schema.RequiredMode.REQUIRED, example = "商品模块")
  64 + @ExcelProperty("操作模块")
  65 + private String operateModule;
  66 +
  67 + @Schema(description = "操作名", requiredMode = Schema.RequiredMode.REQUIRED, example = "创建商品")
  68 + @ExcelProperty("操作名")
  69 + private String operateName;
  70 +
  71 + @Schema(description = "操作分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  72 + @ExcelProperty(value = "操作分类", converter = DictConvert.class)
  73 + @DictFormat(DictTypeConstants.OPERATE_TYPE)
  74 + private Integer operateType;
  75 +
59 @Schema(description = "开始请求时间", requiredMode = Schema.RequiredMode.REQUIRED) 76 @Schema(description = "开始请求时间", requiredMode = Schema.RequiredMode.REQUIRED)
60 @ExcelProperty("开始请求时间") 77 @ExcelProperty("开始请求时间")
61 private LocalDateTime beginTime; 78 private LocalDateTime beginTime;
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java
1 package cn.iocoder.yudao.module.infra.dal.dataobject.logger; 1 package cn.iocoder.yudao.module.infra.dal.dataobject.logger;
2 2
  3 +import cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum;
3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 4 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; 6 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@@ -71,6 +72,10 @@ public class ApiAccessLogDO extends BaseDO { @@ -71,6 +72,10 @@ public class ApiAccessLogDO extends BaseDO {
71 */ 72 */
72 private String requestParams; 73 private String requestParams;
73 /** 74 /**
  75 + * 响应结果
  76 + */
  77 + private String responseBody;
  78 + /**
74 * 用户 IP 79 * 用户 IP
75 */ 80 */
76 private String userIp; 81 private String userIp;
@@ -82,6 +87,21 @@ public class ApiAccessLogDO extends BaseDO { @@ -82,6 +87,21 @@ public class ApiAccessLogDO extends BaseDO {
82 // ========== 执行相关字段 ========== 87 // ========== 执行相关字段 ==========
83 88
84 /** 89 /**
  90 + * 操作模块
  91 + */
  92 + private String operateModule;
  93 + /**
  94 + * 操作名
  95 + */
  96 + private String operateName;
  97 + /**
  98 + * 操作分类
  99 + *
  100 + * 枚举 {@link OperateTypeEnum}
  101 + */
  102 + private Integer operateType;
  103 +
  104 + /**
85 * 开始请求时间 105 * 开始请求时间
86 */ 106 */
87 private LocalDateTime beginTime; 107 private LocalDateTime beginTime;
@@ -93,6 +113,7 @@ public class ApiAccessLogDO extends BaseDO { @@ -93,6 +113,7 @@ public class ApiAccessLogDO extends BaseDO {
93 * 执行时长,单位:毫秒 113 * 执行时长,单位:毫秒
94 */ 114 */
95 private Integer duration; 115 private Integer duration;
  116 +
96 /** 117 /**
97 * 结果码 118 * 结果码
98 * 119 *
yudao-module-infra/yudao-module-infra-biz/src/test/resources/sql/create_tables.sql
@@ -94,8 +94,12 @@ CREATE TABLE IF NOT EXISTS &quot;infra_api_access_log&quot; ( @@ -94,8 +94,12 @@ CREATE TABLE IF NOT EXISTS &quot;infra_api_access_log&quot; (
94 "request_method" varchar(16) not null default '', 94 "request_method" varchar(16) not null default '',
95 "request_url" varchar(255) not null default '', 95 "request_url" varchar(255) not null default '',
96 "request_params" varchar(8000) not null default '', 96 "request_params" varchar(8000) not null default '',
  97 + "response_body" varchar(8000) not null default '',
97 "user_ip" varchar(50) not null, 98 "user_ip" varchar(50) not null,
98 "user_agent" varchar(512) not null, 99 "user_agent" varchar(512) not null,
  100 + `operate_module` varchar(50) NOT NULL,
  101 + `operate_name` varchar(50) NOT NULL,
  102 + `operate_type` bigint(4) NOT NULL DEFAULT '0',
99 "begin_time" timestamp not null, 103 "begin_time" timestamp not null,
100 "end_time" timestamp not null, 104 "end_time" timestamp not null,
101 "duration" integer not null, 105 "duration" integer not null,
@@ -108,7 +112,7 @@ CREATE TABLE IF NOT EXISTS &quot;infra_api_access_log&quot; ( @@ -108,7 +112,7 @@ CREATE TABLE IF NOT EXISTS &quot;infra_api_access_log&quot; (
108 "deleted" bit not null default false, 112 "deleted" bit not null default false,
109 "tenant_id" bigint not null default '0', 113 "tenant_id" bigint not null default '0',
110 primary key ("id") 114 primary key ("id")
111 - ) COMMENT 'API 访问日志表'; 115 +) COMMENT 'API 访问日志表';
112 116
113 CREATE TABLE IF NOT EXISTS "infra_api_error_log" ( 117 CREATE TABLE IF NOT EXISTS "infra_api_error_log" (
114 "id" bigint not null GENERATED BY DEFAULT AS IDENTITY, 118 "id" bigint not null GENERATED BY DEFAULT AS IDENTITY,
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java
1 package cn.iocoder.yudao.module.system.controller.admin.notify; 1 package cn.iocoder.yudao.module.system.controller.admin.notify;
2 2
  3 +import cn.iocoder.yudao.framework.apilog.core.annotations.ApiAccessLog;
3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 4 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 5 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -88,6 +89,7 @@ public class NotifyMessageController { @@ -88,6 +89,7 @@ public class NotifyMessageController {
88 89
89 @GetMapping("/get-unread-count") 90 @GetMapping("/get-unread-count")
90 @Operation(summary = "获得当前用户的未读站内信数量") 91 @Operation(summary = "获得当前用户的未读站内信数量")
  92 + @ApiAccessLog(enable = false) // 由于前端会不断轮询该接口,记录日志没有意义
91 public CommonResult<Long> getUnreadNotifyMessageCount() { 93 public CommonResult<Long> getUnreadNotifyMessageCount() {
92 return success(notifyMessageService.getUnreadNotifyMessageCount( 94 return success(notifyMessageService.getUnreadNotifyMessageCount(
93 getLoginUserId(), UserTypeEnum.ADMIN.getValue())); 95 getLoginUserId(), UserTypeEnum.ADMIN.getValue()));