Commit ffb0358ce23ecf5d0f53ebb977a0d6fe81df5af7

Authored by xingyu
2 parents 6b1f3140 50598ea2

Merge remote-tracking branch 'origin/master' into feature/springdoc

Showing 182 changed files with 6620 additions and 809 deletions
README.md
... ... @@ -91,6 +91,7 @@
91 91 | 🚀 | 租户套餐 | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限 |
92 92 | | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 |
93 93 | 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 |
  94 +| 🚀 | 邮件管理 | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台 |
94 95 | 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
95 96 | ⭐️ | 登录日志 | 系统登录日志记录查询,包含登录异常 |
96 97 | 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 |
... ...
sql/mysql/ruoyi-vue-pro.sql
... ... @@ -11,7 +11,7 @@
11 11 Target Server Version : 80026
12 12 File Encoding : 65001
13 13  
14   - Date: 17/01/2023 23:47:35
  14 + Date: 27/01/2023 20:57:31
15 15 */
16 16  
17 17 SET NAMES utf8mb4;
... ... @@ -300,7 +300,7 @@ CREATE TABLE `bpm_form` (
300 300 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
301 301 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
302 302 PRIMARY KEY (`id`) USING BTREE
303   -) ENGINE = InnoDB AUTO_INCREMENT = 20 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '工作流的表单定义';
  303 +) ENGINE = InnoDB AUTO_INCREMENT = 24 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '工作流的表单定义';
304 304  
305 305 -- ----------------------------
306 306 -- Records of bpm_form
... ... @@ -329,7 +329,7 @@ CREATE TABLE `bpm_oa_leave` (
329 329 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
330 330 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
331 331 PRIMARY KEY (`id`) USING BTREE
332   -) ENGINE = InnoDB AUTO_INCREMENT = 33 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OA 请假申请表';
  332 +) ENGINE = InnoDB AUTO_INCREMENT = 35 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OA 请假申请表';
333 333  
334 334 -- ----------------------------
335 335 -- Records of bpm_oa_leave
... ... @@ -359,7 +359,7 @@ CREATE TABLE `bpm_process_definition_ext` (
359 359 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
360 360 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
361 361 PRIMARY KEY (`id`) USING BTREE
362   -) ENGINE = InnoDB AUTO_INCREMENT = 135 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Bpm 流程定义的拓展表\n';
  362 +) ENGINE = InnoDB AUTO_INCREMENT = 140 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Bpm 流程定义的拓展表\n';
363 363  
364 364 -- ----------------------------
365 365 -- Records of bpm_process_definition_ext
... ... @@ -389,7 +389,7 @@ CREATE TABLE `bpm_process_instance_ext` (
389 389 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
390 390 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
391 391 PRIMARY KEY (`id`) USING BTREE
392   -) ENGINE = InnoDB AUTO_INCREMENT = 290 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '工作流的流程实例的拓展';
  392 +) ENGINE = InnoDB AUTO_INCREMENT = 295 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '工作流的流程实例的拓展';
393 393  
394 394 -- ----------------------------
395 395 -- Records of bpm_process_instance_ext
... ... @@ -415,7 +415,7 @@ CREATE TABLE `bpm_task_assign_rule` (
415 415 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
416 416 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
417 417 PRIMARY KEY (`id`) USING BTREE
418   -) ENGINE = InnoDB AUTO_INCREMENT = 265 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Bpm 任务规则表';
  418 +) ENGINE = InnoDB AUTO_INCREMENT = 274 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Bpm 任务规则表';
419 419  
420 420 -- ----------------------------
421 421 -- Records of bpm_task_assign_rule
... ... @@ -444,7 +444,7 @@ CREATE TABLE `bpm_task_ext` (
444 444 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
445 445 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
446 446 PRIMARY KEY (`id`) USING BTREE
447   -) ENGINE = InnoDB AUTO_INCREMENT = 341 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '工作流的流程任务的拓展表';
  447 +) ENGINE = InnoDB AUTO_INCREMENT = 350 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '工作流的流程任务的拓展表';
448 448  
449 449 -- ----------------------------
450 450 -- Records of bpm_task_ext
... ... @@ -469,7 +469,7 @@ CREATE TABLE `bpm_user_group` (
469 469 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
470 470 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
471 471 PRIMARY KEY (`id`) USING BTREE
472   -) ENGINE = InnoDB AUTO_INCREMENT = 111 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户组';
  472 +) ENGINE = InnoDB AUTO_INCREMENT = 113 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户组';
473 473  
474 474 -- ----------------------------
475 475 -- Records of bpm_user_group
... ... @@ -546,7 +546,7 @@ CREATE TABLE `infra_api_error_log` (
546 546 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
547 547 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
548 548 PRIMARY KEY (`id`) USING BTREE
549   -) ENGINE = InnoDB AUTO_INCREMENT = 931 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
  549 +) ENGINE = InnoDB AUTO_INCREMENT = 949 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
550 550  
551 551 -- ----------------------------
552 552 -- Records of infra_api_error_log
... ... @@ -584,7 +584,7 @@ CREATE TABLE `infra_codegen_column` (
584 584 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
585 585 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
586 586 PRIMARY KEY (`id`) USING BTREE
587   -) ENGINE = InnoDB AUTO_INCREMENT = 1533 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
  587 +) ENGINE = InnoDB AUTO_INCREMENT = 1582 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表字段定义';
588 588  
589 589 -- ----------------------------
590 590 -- Records of infra_codegen_column
... ... @@ -616,7 +616,7 @@ CREATE TABLE `infra_codegen_table` (
616 616 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
617 617 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
618 618 PRIMARY KEY (`id`) USING BTREE
619   -) ENGINE = InnoDB AUTO_INCREMENT = 119 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
  619 +) ENGINE = InnoDB AUTO_INCREMENT = 122 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '代码生成表定义';
620 620  
621 621 -- ----------------------------
622 622 -- Records of infra_codegen_table
... ... @@ -699,7 +699,7 @@ CREATE TABLE `infra_file` (
699 699 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
700 700 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
701 701 PRIMARY KEY (`id`) USING BTREE
702   -) ENGINE = InnoDB AUTO_INCREMENT = 355 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
  702 +) ENGINE = InnoDB AUTO_INCREMENT = 356 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
703 703  
704 704 -- ----------------------------
705 705 -- Records of infra_file
... ... @@ -1191,7 +1191,7 @@ CREATE TABLE `system_dict_data` (
1191 1191 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
1192 1192 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1193 1193 PRIMARY KEY (`id`) USING BTREE
1194   -) ENGINE = InnoDB AUTO_INCREMENT = 1213 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
  1194 +) ENGINE = InnoDB AUTO_INCREMENT = 1227 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
1195 1195  
1196 1196 -- ----------------------------
1197 1197 -- Records of system_dict_data
... ... @@ -1391,6 +1391,10 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
1391 1391 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1220, 8, '地理位置', 'location', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 地理位置', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:23:51', b'0');
1392 1392 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1221, 9, '链接', 'link', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 链接', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:24:49', b'0');
1393 1393 INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1222, 10, '事件', 'event', 'mp_message_type', 0, 'default', '', '公众号的消息类型 - 事件', '1', '2023-01-17 22:17:32', '1', '2023-01-17 14:24:49', b'0');
  1394 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1223, 0, '初始化', '0', 'system_mail_send_status', 0, 'primary', '', '邮件发送状态 - 初始化\n', '1', '2023-01-26 09:53:49', '1', '2023-01-26 16:36:14', b'0');
  1395 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1224, 10, '发送成功', '10', 'system_mail_send_status', 0, 'success', '', '邮件发送状态 - 发送成功', '1', '2023-01-26 09:54:28', '1', '2023-01-26 16:36:22', b'0');
  1396 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1225, 20, '发送失败', '20', 'system_mail_send_status', 0, 'danger', '', '邮件发送状态 - 发送失败', '1', '2023-01-26 09:54:50', '1', '2023-01-26 16:36:26', b'0');
  1397 +INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1226, 30, '不发送', '30', 'system_mail_send_status', 0, 'info', '', '邮件发送状态 - 不发送', '1', '2023-01-26 09:55:06', '1', '2023-01-26 16:36:36', b'0');
1394 1398 COMMIT;
1395 1399  
1396 1400 -- ----------------------------
... ... @@ -1411,7 +1415,7 @@ CREATE TABLE `system_dict_type` (
1411 1415 `deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
1412 1416 PRIMARY KEY (`id`) USING BTREE,
1413 1417 UNIQUE INDEX `dict_type`(`type` ASC) USING BTREE
1414   -) ENGINE = InnoDB AUTO_INCREMENT = 165 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
  1418 +) ENGINE = InnoDB AUTO_INCREMENT = 167 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
1415 1419  
1416 1420 -- ----------------------------
1417 1421 -- Records of system_dict_type
... ... @@ -1474,6 +1478,7 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat
1474 1478 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (163, '交易订单项的售后状态', 'trade_order_item_after_sale_status', 0, '交易订单项的售后状态', '1', '2022-12-10 20:58:08', '1', '2022-12-10 20:58:08', b'0', NULL);
1475 1479 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (164, '公众号自动回复的请求关键字匹配模式', 'mp_auto_reply_request_match', 0, '公众号自动回复的请求关键字匹配模式', '1', '2023-01-16 23:29:56', '1', '2023-01-16 23:29:56', b'0', '1970-01-01 00:00:00');
1476 1480 INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (165, '公众号的消息类型', 'mp_message_type', 0, '公众号的消息类型', '1', '2023-01-17 22:17:09', '1', '2023-01-17 22:17:09', b'0', '1970-01-01 00:00:00');
  1481 +INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (166, '邮件发送状态', 'system_mail_send_status', 0, '邮件发送状态', '1', '2023-01-26 09:53:13', '1', '2023-01-26 09:53:13', b'0', '1970-01-01 00:00:00');
1477 1482 COMMIT;
1478 1483  
1479 1484 -- ----------------------------
... ... @@ -1522,7 +1527,7 @@ CREATE TABLE `system_login_log` (
1522 1527 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1523 1528 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
1524 1529 PRIMARY KEY (`id`) USING BTREE
1525   -) ENGINE = InnoDB AUTO_INCREMENT = 1963 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
  1530 +) ENGINE = InnoDB AUTO_INCREMENT = 1980 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
1526 1531  
1527 1532 -- ----------------------------
1528 1533 -- Records of system_login_log
... ... @@ -1531,6 +1536,102 @@ BEGIN;
1531 1536 COMMIT;
1532 1537  
1533 1538 -- ----------------------------
  1539 +-- Table structure for system_mail_account
  1540 +-- ----------------------------
  1541 +DROP TABLE IF EXISTS `system_mail_account`;
  1542 +CREATE TABLE `system_mail_account` (
  1543 + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
  1544 + `mail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮箱',
  1545 + `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '用户名',
  1546 + `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '密码',
  1547 + `host` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'SMTP 服务器域名',
  1548 + `port` int NOT NULL COMMENT 'SMTP 服务器端口',
  1549 + `ssl_enable` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否开启 SSL',
  1550 + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
  1551 + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  1552 + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
  1553 + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  1554 + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  1555 + PRIMARY KEY (`id`) USING BTREE
  1556 +) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮箱账号表';
  1557 +
  1558 +-- ----------------------------
  1559 +-- Records of system_mail_account
  1560 +-- ----------------------------
  1561 +BEGIN;
  1562 +INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '7684413@qq.com', '7684413@qq.com', '123457', '127.0.0.1', 8080, b'0', '1', '2023-01-25 17:39:52', '1', '2023-01-26 22:59:04', b'0');
  1563 +INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, 'ydym_test@163.com', 'ydym_test@163.com', 'WBZTEINMIFVRYSOE', 'smtp.163.com', 465, b'1', '1', '2023-01-26 01:26:03', '1', '2023-01-26 01:26:03', b'0');
  1564 +INSERT INTO `system_mail_account` (`id`, `mail`, `username`, `password`, `host`, `port`, `ssl_enable`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3, '76854114@qq.com', '3335', '11234', 'yunai1.cn', 466, b'0', '1', '2023-01-27 15:06:38', '1', '2023-01-27 07:08:36', b'1');
  1565 +COMMIT;
  1566 +
  1567 +-- ----------------------------
  1568 +-- Table structure for system_mail_log
  1569 +-- ----------------------------
  1570 +DROP TABLE IF EXISTS `system_mail_log`;
  1571 +CREATE TABLE `system_mail_log` (
  1572 + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
  1573 + `user_id` bigint NULL DEFAULT NULL COMMENT '用户编号',
  1574 + `user_type` tinyint NULL DEFAULT NULL COMMENT '用户类型',
  1575 + `to_mail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '接收邮箱地址',
  1576 + `account_id` bigint NOT NULL COMMENT '邮箱账号编号',
  1577 + `from_mail` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '发送邮箱地址',
  1578 + `template_id` bigint NOT NULL COMMENT '模板编号',
  1579 + `template_code` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板编码',
  1580 + `template_nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '模版发送人名称',
  1581 + `template_title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮件标题',
  1582 + `template_content` varchar(10240) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮件内容',
  1583 + `template_params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '邮件参数',
  1584 + `send_status` tinyint NOT NULL DEFAULT 0 COMMENT '发送状态',
  1585 + `send_time` datetime NULL DEFAULT NULL COMMENT '发送时间',
  1586 + `send_message_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发送返回的消息 ID',
  1587 + `send_exception` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发送异常',
  1588 + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
  1589 + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  1590 + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
  1591 + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  1592 + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  1593 + PRIMARY KEY (`id`) USING BTREE
  1594 +) ENGINE = InnoDB AUTO_INCREMENT = 354 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮件日志表';
  1595 +
  1596 +-- ----------------------------
  1597 +-- Records of system_mail_log
  1598 +-- ----------------------------
  1599 +BEGIN;
  1600 +COMMIT;
  1601 +
  1602 +-- ----------------------------
  1603 +-- Table structure for system_mail_template
  1604 +-- ----------------------------
  1605 +DROP TABLE IF EXISTS `system_mail_template`;
  1606 +CREATE TABLE `system_mail_template` (
  1607 + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
  1608 + `name` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板名称',
  1609 + `code` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板编码',
  1610 + `account_id` bigint NOT NULL COMMENT '发送的邮箱账号编号',
  1611 + `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '发送人名称',
  1612 + `title` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板标题',
  1613 + `content` varchar(10240) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '模板内容',
  1614 + `params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '参数数组',
  1615 + `status` tinyint NOT NULL COMMENT '开启状态',
  1616 + `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '备注',
  1617 + `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '创建者',
  1618 + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  1619 + `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '更新者',
  1620 + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  1621 + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  1622 + PRIMARY KEY (`id`) USING BTREE
  1623 +) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '邮件模版表';
  1624 +
  1625 +-- ----------------------------
  1626 +-- Records of system_mail_template
  1627 +-- ----------------------------
  1628 +BEGIN;
  1629 +INSERT INTO `system_mail_template` (`id`, `name`, `code`, `account_id`, `nickname`, `title`, `content`, `params`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (13, '后台用户短信登录', 'admin-sms-login', 1, '奥特曼', '你猜我猜', '<p>您的验证码是{code},名字是{name}</p>', '[\"code\",\"name\"]', 0, '3', '1', '2021-10-11 08:10:00', '1', '2023-01-26 23:22:05', b'0');
  1630 +INSERT INTO `system_mail_template` (`id`, `name`, `code`, `account_id`, `nickname`, `title`, `content`, `params`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (14, '测试模版', 'test_01', 2, '芋艿', '一个标题', '<p>你是 {key01} 吗?</p><p><br></p><p>是的话,赶紧 {key02} 一下!</p>', '[\"key01\",\"key02\"]', 0, NULL, '1', '2023-01-26 01:27:40', '1', '2023-01-27 10:32:16', b'0');
  1631 +INSERT INTO `system_mail_template` (`id`, `name`, `code`, `account_id`, `nickname`, `title`, `content`, `params`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (15, '3', '2', 2, '7', '4', '<p>45</p>', '[]', 1, '80', '1', '2023-01-27 15:50:35', '1', '2023-01-27 16:34:49', b'0');
  1632 +COMMIT;
  1633 +
  1634 +-- ----------------------------
1534 1635 -- Table structure for system_menu
1535 1636 -- ----------------------------
1536 1637 DROP TABLE IF EXISTS `system_menu`;
... ... @@ -1553,7 +1654,7 @@ CREATE TABLE `system_menu` (
1553 1654 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
1554 1655 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1555 1656 PRIMARY KEY (`id`) USING BTREE
1556   -) ENGINE = InnoDB AUTO_INCREMENT = 2125 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
  1657 +) ENGINE = InnoDB AUTO_INCREMENT = 2144 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
1557 1658  
1558 1659 -- ----------------------------
1559 1660 -- Records of system_menu
... ... @@ -1776,7 +1877,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
1776 1877 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1250, '敏感词更新', 'system:sensitive-word:update', 3, 3, 1247, '', '', '', 0, b'1', b'1', '', '2022-04-07 16:55:03', '', '2022-04-20 17:03:10', b'0');
1777 1878 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1251, '敏感词删除', 'system:sensitive-word:delete', 3, 4, 1247, '', '', '', 0, b'1', b'1', '', '2022-04-07 16:55:03', '', '2022-04-20 17:03:10', b'0');
1778 1879 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1252, '敏感词导出', 'system:sensitive-word:export', 3, 5, 1247, '', '', '', 0, b'1', b'1', '', '2022-04-07 16:55:03', '', '2022-04-20 17:03:10', b'0');
1779   -INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'people', NULL, 0, b'1', b'1', '1', '2022-04-23 01:03:15', '1', '2022-06-22 13:35:05', b'0');
  1880 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1254, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'people', NULL, 0, b'1', b'1', '1', '2022-04-23 01:03:15', '1', '2023-01-25 20:05:55', b'0');
1780 1881 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1255, '数据源配置', '', 2, 1, 2, 'data-source-config', 'rate', 'infra/dataSourceConfig/index', 0, b'1', b'1', '', '2022-04-27 14:37:32', '1', '2022-04-27 22:42:06', b'0');
1781 1882 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1256, '数据源配置查询', 'infra:data-source-config:query', 3, 1, 1255, '', '', '', 0, b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');
1782 1883 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1257, '数据源配置创建', 'infra:data-source-config:create', 3, 2, 1255, '', '', '', 0, b'1', b'1', '', '2022-04-27 14:37:32', '', '2022-04-27 14:37:32', b'0');
... ... @@ -1908,6 +2009,20 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
1908 2009 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2127, '删除菜单', 'mp:menu:delete', 3, 2, 2119, '', '', '', 0, b'1', b'1', '1', '2023-01-17 23:06:16', '1', '2023-01-17 23:06:16', b'0');
1909 2010 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2128, '查询消息', 'mp:message:query', 3, 0, 2103, '', '', '', 0, b'1', b'1', '1', '2023-01-17 23:07:14', '1', '2023-01-17 23:07:14', b'0');
1910 2011 INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2129, '发送消息', 'mp:message:send', 3, 1, 2103, '', '', '', 0, b'1', b'1', '1', '2023-01-17 23:07:26', '1', '2023-01-17 23:07:26', b'0');
  2012 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2130, '邮箱管理', '', 2, 11, 1, 'mail', 'email', NULL, 0, b'1', b'1', '1', '2023-01-25 17:27:44', '1', '2023-01-25 17:27:44', b'0');
  2013 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2131, '邮箱账号', '', 2, 0, 2130, 'mail-account', 'user', 'system/mail/account/index', 0, b'1', b'1', '', '2023-01-25 09:33:48', '1', '2023-01-26 16:37:37', b'0');
  2014 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2132, '账号查询', 'system:mail-account:query', 3, 1, 2131, '', '', '', 0, b'1', b'1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', b'0');
  2015 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2133, '账号创建', 'system:mail-account:create', 3, 2, 2131, '', '', '', 0, b'1', b'1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', b'0');
  2016 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2134, '账号更新', 'system:mail-account:update', 3, 3, 2131, '', '', '', 0, b'1', b'1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', b'0');
  2017 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2135, '账号删除', 'system:mail-account:delete', 3, 4, 2131, '', '', '', 0, b'1', b'1', '', '2023-01-25 09:33:48', '', '2023-01-25 09:33:48', b'0');
  2018 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2136, '邮件模版', '', 2, 0, 2130, 'mail-template', 'education', 'system/mail/template/index', 0, b'1', b'1', '', '2023-01-25 12:05:31', '1', '2023-01-26 16:38:35', b'0');
  2019 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2137, '模版查询', 'system:mail-template:query', 3, 1, 2136, '', '', '', 0, b'1', b'1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', b'0');
  2020 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2138, '模版创建', 'system:mail-template:create', 3, 2, 2136, '', '', '', 0, b'1', b'1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', b'0');
  2021 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2139, '模版更新', 'system:mail-template:update', 3, 3, 2136, '', '', '', 0, b'1', b'1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', b'0');
  2022 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2140, '模版删除', 'system:mail-template:delete', 3, 4, 2136, '', '', '', 0, b'1', b'1', '', '2023-01-25 12:05:31', '', '2023-01-25 12:05:31', b'0');
  2023 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2141, '邮件记录', '', 2, 0, 2130, 'mail-log', 'log', 'system/mail/log/index', 0, b'1', b'1', '', '2023-01-26 02:16:50', '1', '2023-01-26 16:38:27', b'0');
  2024 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2142, '日志查询', 'system:mail-log:query', 3, 1, 2141, '', '', '', 0, b'1', b'1', '', '2023-01-26 02:16:50', '', '2023-01-26 02:16:50', b'0');
  2025 +INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `status`, `visible`, `keep_alive`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2143, '发送测试邮件', 'system:mail-template:send-mail', 3, 5, 2136, '', '', '', 0, b'1', b'1', '1', '2023-01-26 23:29:15', '1', '2023-01-26 23:29:15', b'0');
1911 2026 COMMIT;
1912 2027  
1913 2028 -- ----------------------------
... ... @@ -1958,7 +2073,7 @@ CREATE TABLE `system_oauth2_access_token` (
1958 2073 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
1959 2074 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
1960 2075 PRIMARY KEY (`id`) USING BTREE
1961   -) ENGINE = InnoDB AUTO_INCREMENT = 1083 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
  2076 +) ENGINE = InnoDB AUTO_INCREMENT = 1214 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
1962 2077  
1963 2078 -- ----------------------------
1964 2079 -- Records of system_oauth2_access_token
... ... @@ -2080,7 +2195,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
2080 2195 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
2081 2196 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
2082 2197 PRIMARY KEY (`id`) USING BTREE
2083   -) ENGINE = InnoDB AUTO_INCREMENT = 571 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
  2198 +) ENGINE = InnoDB AUTO_INCREMENT = 588 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
2084 2199  
2085 2200 -- ----------------------------
2086 2201 -- Records of system_oauth2_refresh_token
... ... @@ -2120,7 +2235,7 @@ CREATE TABLE `system_operate_log` (
2120 2235 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
2121 2236 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
2122 2237 PRIMARY KEY (`id`) USING BTREE
2123   -) ENGINE = InnoDB AUTO_INCREMENT = 4018 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
  2238 +) ENGINE = InnoDB AUTO_INCREMENT = 4177 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录';
2124 2239  
2125 2240 -- ----------------------------
2126 2241 -- Records of system_operate_log
... ... @@ -2212,7 +2327,7 @@ CREATE TABLE `system_role_menu` (
2212 2327 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
2213 2328 `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
2214 2329 PRIMARY KEY (`id`) USING BTREE
2215   -) ENGINE = InnoDB AUTO_INCREMENT = 1991 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色和菜单关联表';
  2330 +) ENGINE = InnoDB AUTO_INCREMENT = 2185 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色和菜单关联表';
2216 2331  
2217 2332 -- ----------------------------
2218 2333 -- Records of system_role_menu
... ... @@ -2628,6 +2743,194 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
2628 2743 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1988, 118, 1018, '1', '2022-12-30 11:47:52', '1', '2022-12-30 11:47:52', b'0', 129);
2629 2744 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1989, 118, 1019, '1', '2022-12-30 11:47:52', '1', '2022-12-30 11:47:52', b'0', 129);
2630 2745 INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1990, 118, 1020, '1', '2022-12-30 11:47:52', '1', '2022-12-30 11:47:52', b'0', 129);
  2746 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1991, 2, 1024, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2747 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1992, 2, 1025, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2748 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1993, 2, 1026, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2749 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1994, 2, 1027, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2750 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1995, 2, 1028, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2751 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1996, 2, 1029, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2752 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1997, 2, 1030, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2753 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1998, 2, 1031, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2754 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1999, 2, 1032, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2755 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2000, 2, 1033, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2756 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2001, 2, 1034, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2757 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2002, 2, 1035, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2758 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2003, 2, 1036, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2759 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2004, 2, 1037, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2760 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2005, 2, 1038, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2761 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2006, 2, 1039, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2762 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2007, 2, 1040, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2763 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2008, 2, 1042, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2764 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2009, 2, 1043, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2765 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2010, 2, 1045, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2766 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2011, 2, 1046, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2767 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2012, 2, 1048, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2768 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2013, 2, 1050, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2769 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2014, 2, 1051, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2770 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2015, 2, 1052, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2771 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2016, 2, 1053, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2772 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2017, 2, 1054, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2773 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2018, 2, 1056, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2774 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2019, 2, 1057, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2775 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2020, 2, 1058, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2776 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2021, 2, 2083, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2777 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2022, 2, 1059, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2778 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2023, 2, 1060, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2779 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2024, 2, 1063, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2780 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2025, 2, 1064, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2781 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2026, 2, 1065, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2782 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2027, 2, 1066, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2783 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2028, 2, 1067, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2784 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2029, 2, 1070, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2785 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2030, 2, 1071, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2786 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2031, 2, 1072, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2787 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2032, 2, 1073, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2788 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2033, 2, 1074, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2789 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2034, 2, 1075, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2790 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2035, 2, 1076, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2791 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2036, 2, 1082, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2792 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2037, 2, 1085, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2793 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2038, 2, 1086, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2794 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2039, 2, 1087, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2795 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2040, 2, 1088, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2796 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2041, 2, 1089, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2797 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2042, 2, 1091, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2798 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2043, 2, 1092, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2799 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2044, 2, 1095, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2800 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2045, 2, 1096, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2801 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2046, 2, 1097, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2802 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2047, 2, 1098, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2803 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2048, 2, 1101, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2804 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2049, 2, 1102, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2805 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2050, 2, 1103, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2806 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2051, 2, 1104, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2807 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2052, 2, 1105, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2808 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2053, 2, 1106, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2809 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2054, 2, 1108, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2810 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2055, 2, 1109, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2811 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2056, 2, 1111, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2812 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2057, 2, 1112, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2813 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2058, 2, 1113, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2814 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2059, 2, 1114, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2815 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2060, 2, 1115, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2816 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2061, 2, 1127, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2817 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2062, 2, 1128, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2818 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2063, 2, 1129, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2819 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2064, 2, 1130, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2820 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2065, 2, 1131, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2821 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2066, 2, 1132, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2822 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2067, 2, 1133, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2823 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2068, 2, 1134, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2824 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2069, 2, 1135, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2825 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2070, 2, 1136, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2826 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2071, 2, 1137, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2827 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2072, 2, 114, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2828 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2073, 2, 1139, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2829 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2074, 2, 115, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2830 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2075, 2, 1140, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2831 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2076, 2, 116, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2832 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2077, 2, 1141, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2833 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2078, 2, 1142, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2834 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2079, 2, 1143, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2835 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2080, 2, 1150, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2836 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2081, 2, 1161, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2837 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2082, 2, 1162, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2838 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2083, 2, 1163, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2839 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2084, 2, 1164, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2840 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2085, 2, 1165, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2841 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2086, 2, 1166, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2842 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2087, 2, 1173, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2843 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2088, 2, 1174, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2844 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2089, 2, 1175, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2845 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2090, 2, 1176, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2846 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2091, 2, 1177, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2847 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2092, 2, 1178, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2848 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2093, 2, 1179, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2849 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2094, 2, 1180, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2850 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2095, 2, 1181, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2851 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2096, 2, 1182, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2852 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2097, 2, 1183, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2853 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2098, 2, 1184, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2854 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2099, 2, 1226, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2855 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2100, 2, 1227, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2856 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2101, 2, 1228, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2857 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2102, 2, 1229, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2858 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2103, 2, 1237, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2859 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2104, 2, 1238, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2860 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2105, 2, 1239, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2861 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2106, 2, 1240, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2862 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2107, 2, 1241, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2863 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2108, 2, 1242, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2864 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2109, 2, 1243, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2865 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2110, 2, 1247, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2866 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2111, 2, 1248, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2867 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2112, 2, 1249, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2868 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2113, 2, 1250, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2869 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2114, 2, 1251, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2870 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2115, 2, 1252, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2871 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2116, 2, 1254, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2872 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2117, 2, 1255, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2873 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2118, 2, 1256, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2874 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2119, 2, 1257, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2875 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2120, 2, 1258, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2876 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2121, 2, 1259, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2877 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2122, 2, 1260, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2878 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2123, 2, 1261, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2879 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2124, 2, 1263, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2880 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2125, 2, 1264, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2881 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2126, 2, 1265, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2882 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2127, 2, 1266, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2883 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2128, 2, 1267, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2884 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2129, 2, 1001, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2885 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2130, 2, 1002, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2886 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2131, 2, 1003, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2887 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2132, 2, 1004, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2888 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2133, 2, 1005, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2889 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2134, 2, 1006, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2890 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2135, 2, 1007, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2891 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2136, 2, 1008, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2892 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2137, 2, 1009, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2893 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2138, 2, 1010, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2894 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2139, 2, 1011, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2895 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2140, 2, 1012, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2896 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2141, 2, 1013, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2897 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2142, 2, 1014, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2898 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2143, 2, 1015, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2899 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2144, 2, 1016, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2900 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2145, 2, 1017, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2901 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2146, 2, 1018, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2902 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2147, 2, 1019, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2903 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2148, 2, 1020, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2904 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2149, 2, 1021, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2905 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2150, 2, 1022, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2906 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2151, 2, 1023, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
  2907 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2152, 2, 1281, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2908 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2153, 2, 1282, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2909 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2154, 2, 2000, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2910 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2155, 2, 2002, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2911 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2156, 2, 2003, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2912 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2157, 2, 2004, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2913 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2158, 2, 2005, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2914 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2159, 2, 2006, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2915 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2160, 2, 2008, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2916 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2161, 2, 2009, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2917 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2162, 2, 2010, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2918 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2163, 2, 2011, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2919 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2164, 2, 2012, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2920 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2170, 2, 2019, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2921 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2171, 2, 2020, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2922 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2172, 2, 2021, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2923 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2173, 2, 2022, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2924 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2174, 2, 2023, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2925 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2175, 2, 2025, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2926 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2177, 2, 2027, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2927 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2178, 2, 2028, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2928 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2179, 2, 2029, '1', '2023-01-25 08:42:58', '1', '2023-01-25 08:42:58', b'0', 1);
  2929 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2180, 2, 2014, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);
  2930 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2181, 2, 2015, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);
  2931 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2182, 2, 2016, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);
  2932 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2183, 2, 2017, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);
  2933 +INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2184, 2, 2018, '1', '2023-01-25 08:43:12', '1', '2023-01-25 08:43:12', b'0', 1);
2631 2934 COMMIT;
2632 2935  
2633 2936 -- ----------------------------
... ... @@ -2752,7 +3055,7 @@ CREATE TABLE `system_sms_log` (
2752 3055 `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
2753 3056 `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
2754 3057 PRIMARY KEY (`id`) USING BTREE
2755   -) ENGINE = InnoDB AUTO_INCREMENT = 339 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
  3058 +) ENGINE = InnoDB AUTO_INCREMENT = 347 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
2756 3059  
2757 3060 -- ----------------------------
2758 3061 -- Records of system_sms_log
... ... @@ -3050,7 +3353,7 @@ CREATE TABLE `system_users` (
3050 3353 -- Records of system_users
3051 3354 -- ----------------------------
3052 3355 BEGIN;
3053   -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', '15612345678', 1, 'http://test.yudao.iocoder.cn/e1fdd7271685ec143a0900681606406621717a666ad0b2798b096df41422b32f.png', 0, '127.0.0.1', '2023-01-17 12:33:07', 'admin', '2021-01-05 17:03:47', NULL, '2023-01-17 12:33:07', b'0', 1);
  3356 +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', '15612345678', 1, 'http://test.yudao.iocoder.cn/e1fdd7271685ec143a0900681606406621717a666ad0b2798b096df41422b32f.png', 0, '0:0:0:0:0:0:0:1', '2023-01-27 14:31:00', 'admin', '2021-01-05 17:03:47', NULL, '2023-01-27 14:31:00', b'0', 1);
3054 3357 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);
3055 3358 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, '127.0.0.1', '2022-07-08 01:26:27', '', '2021-01-13 23:50:35', NULL, '2022-07-08 01:26:27', b'0', 1);
3056 3359 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$10$GP8zvqHB//TekuzYZSBYAuBQJiNq1.fxQVDYJ.uBCOnWCtDVKE4H6', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, '', 0, '127.0.0.1', '2022-05-28 15:43:17', '', '2021-01-21 02:13:53', NULL, '2022-07-09 09:00:33', b'0', 1);
... ...
yudao-dependencies/pom.xml
... ... @@ -143,6 +143,11 @@
143 143 <artifactId>yudao-spring-boot-starter-captcha</artifactId>
144 144 <version>${revision}</version>
145 145 </dependency>
  146 + <dependency>
  147 + <groupId>cn.iocoder.boot</groupId>
  148 + <artifactId>yudao-spring-boot-starter-desensitize</artifactId>
  149 + <version>${revision}</version>
  150 + </dependency>
146 151  
147 152 <!-- Spring 核心 -->
148 153 <dependency>
... ...
yudao-framework/pom.xml
... ... @@ -41,6 +41,7 @@
41 41 <module>yudao-spring-boot-starter-flowable</module>
42 42 <module>yudao-spring-boot-starter-captcha</module>
43 43 <module>yudao-spring-boot-starter-websocket</module>
  44 + <module>yudao-spring-boot-starter-desensitize</module>
44 45 </modules>
45 46  
46 47 <artifactId>yudao-framework</artifactId>
... ...
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
... ... @@ -41,6 +41,11 @@ public class LocalDateTimeUtils {
41 41 return LocalDateTime.of(year, mouth, day, 0, 0, 0);
42 42 }
43 43  
  44 + public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1,
  45 + int year2, int mouth2, int day2) {
  46 + return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
  47 + }
  48 +
44 49 /**
45 50 * 判断当前时间是否在该时间范围内
46 51 *
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/pom.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
  3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5 + <modelVersion>4.0.0</modelVersion>
  6 + <parent>
  7 + <artifactId>yudao-framework</artifactId>
  8 + <groupId>cn.iocoder.boot</groupId>
  9 + <version>${revision}</version>
  10 + </parent>
  11 +
  12 + <artifactId>yudao-spring-boot-starter-desensitize</artifactId>
  13 + <description>脱敏组件:支持 JSON 返回数据时,将邮箱、手机等字段进行脱敏</description>
  14 +
  15 + <dependencies>
  16 + <dependency>
  17 + <groupId>cn.iocoder.boot</groupId>
  18 + <artifactId>yudao-common</artifactId>
  19 + </dependency>
  20 +
  21 + <!-- jackson -->
  22 + <dependency>
  23 + <groupId>com.fasterxml.jackson.core</groupId>
  24 + <artifactId>jackson-annotations</artifactId>
  25 + </dependency>
  26 + <dependency>
  27 + <groupId>com.fasterxml.jackson.core</groupId>
  28 + <artifactId>jackson-databind</artifactId>
  29 + </dependency>
  30 +
  31 + <!-- Test 测试相关 -->
  32 + <dependency>
  33 + <groupId>cn.iocoder.boot</groupId>
  34 + <artifactId>yudao-spring-boot-starter-test</artifactId>
  35 + <scope>test</scope>
  36 + </dependency>
  37 + </dependencies>
  38 +</project>
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/annotation/DesensitizeBy.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.base.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;
  4 +import cn.iocoder.yudao.framework.desensitize.core.base.serializer.StringDesensitizeSerializer;
  5 +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  6 +import com.fasterxml.jackson.databind.annotation.JsonSerialize;
  7 +
  8 +import java.lang.annotation.Documented;
  9 +import java.lang.annotation.ElementType;
  10 +import java.lang.annotation.Retention;
  11 +import java.lang.annotation.RetentionPolicy;
  12 +import java.lang.annotation.Target;
  13 +
  14 +/**
  15 + * 顶级脱敏注解,自定义注解需要使用此注解
  16 + *
  17 + * @author gaibu
  18 + */
  19 +@Documented
  20 +@Target(ElementType.ANNOTATION_TYPE)
  21 +@Retention(RetentionPolicy.RUNTIME)
  22 +@JacksonAnnotationsInside // 此注解是其他所有 jackson 注解的元注解,打上了此注解的注解表明是 jackson 注解的一部分
  23 +@JsonSerialize(using = StringDesensitizeSerializer.class) // 指定序列化器
  24 +public @interface DesensitizeBy {
  25 +
  26 + /**
  27 + * 脱敏处理器
  28 + */
  29 + @SuppressWarnings("rawtypes")
  30 + Class<? extends DesensitizationHandler> handler();
  31 +
  32 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/handler/DesensitizationHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.base.handler;
  2 +
  3 +import java.lang.annotation.Annotation;
  4 +
  5 +/**
  6 + * 脱敏处理器接口
  7 + *
  8 + * @author gaibu
  9 + */
  10 +public interface DesensitizationHandler<T extends Annotation> {
  11 +
  12 + /**
  13 + * 脱敏
  14 + *
  15 + * @param origin 原始字符串
  16 + * @param annotation 注解信息
  17 + * @return 脱敏后的字符串
  18 + */
  19 + String desensitize(String origin, T annotation);
  20 +
  21 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/base/serializer/StringDesensitizeSerializer.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.base.serializer;
  2 +
  3 +import cn.hutool.core.annotation.AnnotationUtil;
  4 +import cn.hutool.core.lang.Singleton;
  5 +import cn.hutool.core.util.ArrayUtil;
  6 +import cn.hutool.core.util.ReflectUtil;
  7 +import cn.hutool.core.util.StrUtil;
  8 +import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
  9 +import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;
  10 +import com.fasterxml.jackson.core.JsonGenerator;
  11 +import com.fasterxml.jackson.databind.BeanProperty;
  12 +import com.fasterxml.jackson.databind.JsonSerializer;
  13 +import com.fasterxml.jackson.databind.SerializerProvider;
  14 +import com.fasterxml.jackson.databind.ser.ContextualSerializer;
  15 +import com.fasterxml.jackson.databind.ser.std.StdSerializer;
  16 +import lombok.Getter;
  17 +import lombok.Setter;
  18 +
  19 +import java.io.IOException;
  20 +import java.lang.annotation.Annotation;
  21 +import java.lang.reflect.Field;
  22 +
  23 +/**
  24 + * 脱敏序列化器
  25 + *
  26 + * 实现 JSON 返回数据时,使用 {@link DesensitizationHandler} 对声明脱敏注解的字段,进行脱敏处理。
  27 + *
  28 + * @author gaibu
  29 + */
  30 +@SuppressWarnings("rawtypes")
  31 +public class StringDesensitizeSerializer extends StdSerializer<String> implements ContextualSerializer {
  32 +
  33 + @Getter
  34 + @Setter
  35 + private DesensitizationHandler desensitizationHandler;
  36 +
  37 + protected StringDesensitizeSerializer() {
  38 + super(String.class);
  39 + }
  40 +
  41 + @Override
  42 + public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) {
  43 + DesensitizeBy annotation = beanProperty.getAnnotation(DesensitizeBy.class);
  44 + if (annotation == null) {
  45 + return this;
  46 + }
  47 + // 创建一个 StringDesensitizeSerializer 对象,使用 DesensitizeBy 对应的处理器
  48 + StringDesensitizeSerializer serializer = new StringDesensitizeSerializer();
  49 + serializer.setDesensitizationHandler(Singleton.get(annotation.handler()));
  50 + return serializer;
  51 + }
  52 +
  53 + @Override
  54 + @SuppressWarnings("unchecked")
  55 + public void serialize(String value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException {
  56 + if (StrUtil.isBlank(value)) {
  57 + gen.writeNull();
  58 + return;
  59 + }
  60 + // 获取序列化字段
  61 + Field field = getField(gen);
  62 +
  63 + // 自定义处理器
  64 + DesensitizeBy[] annotations = AnnotationUtil.getCombinationAnnotations(field, DesensitizeBy.class);
  65 + if (ArrayUtil.isEmpty(annotations)) {
  66 + gen.writeString(value);
  67 + return;
  68 + }
  69 + for (Annotation annotation : field.getAnnotations()) {
  70 + if (AnnotationUtil.hasAnnotation(annotation.annotationType(), DesensitizeBy.class)) {
  71 + value = this.desensitizationHandler.desensitize(value, annotation);
  72 + gen.writeString(value);
  73 + return;
  74 + }
  75 + }
  76 + gen.writeString(value);
  77 + }
  78 +
  79 + /**
  80 + * 获取字段
  81 + *
  82 + * @param generator JsonGenerator
  83 + * @return 字段
  84 + */
  85 + private Field getField(JsonGenerator generator) {
  86 + String currentName = generator.getOutputContext().getCurrentName();
  87 + Object currentValue = generator.getCurrentValue();
  88 + Class<?> currentValueClass = currentValue.getClass();
  89 + return ReflectUtil.getField(currentValueClass, currentName);
  90 + }
  91 +
  92 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/package-info.java 0 → 100644
  1 +/**
  2 + * 脱敏组件:支持 JSON 返回数据时,将邮箱、手机等字段进行脱敏
  3 + */
  4 +package cn.iocoder.yudao.framework.desensitize.core;
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/EmailDesensitize.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.regex.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
  4 +import cn.iocoder.yudao.framework.desensitize.core.regex.handler.EmailDesensitizationHandler;
  5 +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  6 +
  7 +import java.lang.annotation.Documented;
  8 +import java.lang.annotation.ElementType;
  9 +import java.lang.annotation.Retention;
  10 +import java.lang.annotation.RetentionPolicy;
  11 +import java.lang.annotation.Target;
  12 +
  13 +/**
  14 + * 邮箱脱敏注解
  15 + *
  16 + * @author gaibu
  17 + */
  18 +@Documented
  19 +@Target({ElementType.FIELD})
  20 +@Retention(RetentionPolicy.RUNTIME)
  21 +@JacksonAnnotationsInside
  22 +@DesensitizeBy(handler = EmailDesensitizationHandler.class)
  23 +public @interface EmailDesensitize {
  24 +
  25 + /**
  26 + * 匹配的正则表达式
  27 + */
  28 + String regex() default "(^.)[^@]*(@.*$)";
  29 +
  30 + /**
  31 + * 替换规则,邮箱;
  32 + *
  33 + * 比如:example@gmail.com 脱敏之后为 e****@gmail.com
  34 + */
  35 + String replacer() default "$1****$2";
  36 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/annotation/RegexDesensitize.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.regex.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
  4 +import cn.iocoder.yudao.framework.desensitize.core.regex.handler.DefaultRegexDesensitizationHandler;
  5 +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  6 +
  7 +import java.lang.annotation.Documented;
  8 +import java.lang.annotation.ElementType;
  9 +import java.lang.annotation.Retention;
  10 +import java.lang.annotation.RetentionPolicy;
  11 +import java.lang.annotation.Target;
  12 +
  13 +/**
  14 + * 正则脱敏注解
  15 + *
  16 + * @author gaibu
  17 + */
  18 +@Documented
  19 +@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
  20 +@Retention(RetentionPolicy.RUNTIME)
  21 +@JacksonAnnotationsInside
  22 +@DesensitizeBy(handler = DefaultRegexDesensitizationHandler.class)
  23 +public @interface RegexDesensitize {
  24 +
  25 + /**
  26 + * 匹配的正则表达式(默认匹配所有)
  27 + */
  28 + String regex() default "^[\\s\\S]*$";
  29 +
  30 + /**
  31 + * 替换规则,会将匹配到的字符串全部替换成 replacer
  32 + *
  33 + * 例如:regex=123; replacer=******
  34 + * 原始字符串 123456789
  35 + * 脱敏后字符串 ******456789
  36 + */
  37 + String replacer() default "******";
  38 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/AbstractRegexDesensitizationHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.regex.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;
  4 +
  5 +import java.lang.annotation.Annotation;
  6 +
  7 +/**
  8 + * 正则表达式脱敏处理器抽象类,已实现通用的方法
  9 + *
  10 + * @author gaibu
  11 + */
  12 +public abstract class AbstractRegexDesensitizationHandler<T extends Annotation>
  13 + implements DesensitizationHandler<T> {
  14 +
  15 + @Override
  16 + public String desensitize(String origin, T annotation) {
  17 + String regex = getRegex(annotation);
  18 + String replacer = getReplacer(annotation);
  19 + return origin.replaceAll(regex, replacer);
  20 + }
  21 +
  22 + /**
  23 + * 获取注解上的 regex 参数
  24 + *
  25 + * @param annotation 注解信息
  26 + * @return 正则表达式
  27 + */
  28 + abstract String getRegex(T annotation);
  29 +
  30 + /**
  31 + * 获取注解上的 replacer 参数
  32 + *
  33 + * @param annotation 注解信息
  34 + * @return 待替换的字符串
  35 + */
  36 + abstract String getReplacer(T annotation);
  37 +
  38 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/DefaultRegexDesensitizationHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.regex.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.regex.annotation.RegexDesensitize;
  4 +
  5 +/**
  6 + * {@link RegexDesensitize} 的正则脱敏处理器
  7 + *
  8 + * @author gaibu
  9 + */
  10 +public class DefaultRegexDesensitizationHandler extends AbstractRegexDesensitizationHandler<RegexDesensitize> {
  11 +
  12 + @Override
  13 + String getRegex(RegexDesensitize annotation) {
  14 + return annotation.regex();
  15 + }
  16 +
  17 + @Override
  18 + String getReplacer(RegexDesensitize annotation) {
  19 + return annotation.replacer();
  20 + }
  21 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/regex/handler/EmailDesensitizationHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.regex.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.regex.annotation.EmailDesensitize;
  4 +
  5 +/**
  6 + * {@link EmailDesensitize} 的脱敏处理器
  7 + *
  8 + * @author gaibu
  9 + */
  10 +public class EmailDesensitizationHandler extends AbstractRegexDesensitizationHandler<EmailDesensitize> {
  11 +
  12 + @Override
  13 + String getRegex(EmailDesensitize annotation) {
  14 + return annotation.regex();
  15 + }
  16 +
  17 + @Override
  18 + String getReplacer(EmailDesensitize annotation) {
  19 + return annotation.replacer();
  20 + }
  21 +
  22 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/BankCardDesensitize.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
  4 +import cn.iocoder.yudao.framework.desensitize.core.slider.handler.BankCardDesensitization;
  5 +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  6 +
  7 +import java.lang.annotation.Documented;
  8 +import java.lang.annotation.ElementType;
  9 +import java.lang.annotation.Retention;
  10 +import java.lang.annotation.RetentionPolicy;
  11 +import java.lang.annotation.Target;
  12 +
  13 +/**
  14 + * 银行卡号
  15 + *
  16 + * @author gaibu
  17 + */
  18 +@Documented
  19 +@Target({ElementType.FIELD})
  20 +@Retention(RetentionPolicy.RUNTIME)
  21 +@JacksonAnnotationsInside
  22 +@DesensitizeBy(handler = BankCardDesensitization.class)
  23 +public @interface BankCardDesensitize {
  24 +
  25 + /**
  26 + * 前缀保留长度
  27 + */
  28 + int prefixKeep() default 6;
  29 +
  30 + /**
  31 + * 后缀保留长度
  32 + */
  33 + int suffixKeep() default 2;
  34 +
  35 + /**
  36 + * 替换规则,银行卡号; 比如:9988002866797031 脱敏之后为 998800********31
  37 + */
  38 + String replacer() default "*";
  39 +
  40 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/CarLicenseDesensitize.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
  4 +import cn.iocoder.yudao.framework.desensitize.core.slider.handler.CarLicenseDesensitization;
  5 +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  6 +
  7 +import java.lang.annotation.Documented;
  8 +import java.lang.annotation.ElementType;
  9 +import java.lang.annotation.Retention;
  10 +import java.lang.annotation.RetentionPolicy;
  11 +import java.lang.annotation.Target;
  12 +
  13 +/**
  14 + * 车牌号
  15 + *
  16 + * @author gaibu
  17 + */
  18 +@Documented
  19 +@Target({ElementType.FIELD})
  20 +@Retention(RetentionPolicy.RUNTIME)
  21 +@JacksonAnnotationsInside
  22 +@DesensitizeBy(handler = CarLicenseDesensitization.class)
  23 +public @interface CarLicenseDesensitize {
  24 +
  25 + /**
  26 + * 前缀保留长度
  27 + */
  28 + int prefixKeep() default 3;
  29 +
  30 + /**
  31 + * 后缀保留长度
  32 + */
  33 + int suffixKeep() default 1;
  34 +
  35 + /**
  36 + * 替换规则,车牌号;比如:粤A66666 脱敏之后为粤A6***6
  37 + */
  38 + String replacer() default "*";
  39 +
  40 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/ChineseNameDesensitize.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
  4 +import cn.iocoder.yudao.framework.desensitize.core.slider.handler.ChineseNameDesensitization;
  5 +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  6 +
  7 +import java.lang.annotation.Documented;
  8 +import java.lang.annotation.ElementType;
  9 +import java.lang.annotation.Retention;
  10 +import java.lang.annotation.RetentionPolicy;
  11 +import java.lang.annotation.Target;
  12 +
  13 +/**
  14 + * 中文名
  15 + *
  16 + * @author gaibu
  17 + */
  18 +@Documented
  19 +@Target({ElementType.FIELD})
  20 +@Retention(RetentionPolicy.RUNTIME)
  21 +@JacksonAnnotationsInside
  22 +@DesensitizeBy(handler = ChineseNameDesensitization.class)
  23 +public @interface ChineseNameDesensitize {
  24 +
  25 + /**
  26 + * 前缀保留长度
  27 + */
  28 + int prefixKeep() default 1;
  29 +
  30 + /**
  31 + * 后缀保留长度
  32 + */
  33 + int suffixKeep() default 0;
  34 +
  35 + /**
  36 + * 替换规则,中文名;比如:刘子豪脱敏之后为刘**
  37 + */
  38 + String replacer() default "*";
  39 +
  40 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/FixedPhoneDesensitize.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
  4 +import cn.iocoder.yudao.framework.desensitize.core.slider.handler.FixedPhoneDesensitization;
  5 +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  6 +
  7 +import java.lang.annotation.Documented;
  8 +import java.lang.annotation.ElementType;
  9 +import java.lang.annotation.Retention;
  10 +import java.lang.annotation.RetentionPolicy;
  11 +import java.lang.annotation.Target;
  12 +
  13 +/**
  14 + * 固定电话
  15 + *
  16 + * @author gaibu
  17 + */
  18 +@Documented
  19 +@Target({ElementType.FIELD})
  20 +@Retention(RetentionPolicy.RUNTIME)
  21 +@JacksonAnnotationsInside
  22 +@DesensitizeBy(handler = FixedPhoneDesensitization.class)
  23 +public @interface FixedPhoneDesensitize {
  24 +
  25 + /**
  26 + * 前缀保留长度
  27 + */
  28 + int prefixKeep() default 4;
  29 +
  30 + /**
  31 + * 后缀保留长度
  32 + */
  33 + int suffixKeep() default 2;
  34 +
  35 + /**
  36 + * 替换规则,固定电话;比如:01086551122 脱敏之后为 0108*****22
  37 + */
  38 + String replacer() default "*";
  39 +
  40 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/IdCardDesensitize.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
  4 +import cn.iocoder.yudao.framework.desensitize.core.slider.handler.IdCardDesensitization;
  5 +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  6 +
  7 +import java.lang.annotation.Documented;
  8 +import java.lang.annotation.ElementType;
  9 +import java.lang.annotation.Retention;
  10 +import java.lang.annotation.RetentionPolicy;
  11 +import java.lang.annotation.Target;
  12 +
  13 +/**
  14 + * 身份证
  15 + *
  16 + * @author gaibu
  17 + */
  18 +@Documented
  19 +@Target({ElementType.FIELD})
  20 +@Retention(RetentionPolicy.RUNTIME)
  21 +@JacksonAnnotationsInside
  22 +@DesensitizeBy(handler = IdCardDesensitization.class)
  23 +public @interface IdCardDesensitize {
  24 +
  25 + /**
  26 + * 前缀保留长度
  27 + */
  28 + int prefixKeep() default 6;
  29 +
  30 + /**
  31 + * 后缀保留长度
  32 + */
  33 + int suffixKeep() default 2;
  34 +
  35 + /**
  36 + * 替换规则,身份证号码;比如:530321199204074611 脱敏之后为 530321**********11
  37 + */
  38 + String replacer() default "*";
  39 +
  40 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/MobileDesensitize.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
  4 +import cn.iocoder.yudao.framework.desensitize.core.slider.handler.MobileDesensitization;
  5 +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  6 +
  7 +import java.lang.annotation.Documented;
  8 +import java.lang.annotation.ElementType;
  9 +import java.lang.annotation.Retention;
  10 +import java.lang.annotation.RetentionPolicy;
  11 +import java.lang.annotation.Target;
  12 +
  13 +/**
  14 + * 手机号
  15 + *
  16 + * @author gaibu
  17 + */
  18 +@Documented
  19 +@Target({ElementType.FIELD})
  20 +@Retention(RetentionPolicy.RUNTIME)
  21 +@JacksonAnnotationsInside
  22 +@DesensitizeBy(handler = MobileDesensitization.class)
  23 +public @interface MobileDesensitize {
  24 +
  25 + /**
  26 + * 前缀保留长度
  27 + */
  28 + int prefixKeep() default 3;
  29 +
  30 + /**
  31 + * 后缀保留长度
  32 + */
  33 + int suffixKeep() default 4;
  34 +
  35 + /**
  36 + * 替换规则,手机号;比如:13248765917 脱敏之后为 132****5917
  37 + */
  38 + String replacer() default "*";
  39 +
  40 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/PasswordDesensitize.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
  4 +import cn.iocoder.yudao.framework.desensitize.core.slider.handler.PasswordDesensitization;
  5 +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  6 +
  7 +import java.lang.annotation.Documented;
  8 +import java.lang.annotation.ElementType;
  9 +import java.lang.annotation.Retention;
  10 +import java.lang.annotation.RetentionPolicy;
  11 +import java.lang.annotation.Target;
  12 +
  13 +/**
  14 + * 密码
  15 + *
  16 + * @author gaibu
  17 + */
  18 +@Documented
  19 +@Target({ElementType.FIELD})
  20 +@Retention(RetentionPolicy.RUNTIME)
  21 +@JacksonAnnotationsInside
  22 +@DesensitizeBy(handler = PasswordDesensitization.class)
  23 +public @interface PasswordDesensitize {
  24 +
  25 + /**
  26 + * 前缀保留长度
  27 + */
  28 + int prefixKeep() default 0;
  29 +
  30 + /**
  31 + * 后缀保留长度
  32 + */
  33 + int suffixKeep() default 0;
  34 +
  35 + /**
  36 + * 替换规则,密码;
  37 + *
  38 + * 比如:123456 脱敏之后为 ******
  39 + */
  40 + String replacer() default "*";
  41 +
  42 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/annotation/SliderDesensitize.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
  4 +import cn.iocoder.yudao.framework.desensitize.core.slider.handler.DefaultDesensitizationHandler;
  5 +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  6 +
  7 +import java.lang.annotation.Documented;
  8 +import java.lang.annotation.ElementType;
  9 +import java.lang.annotation.Retention;
  10 +import java.lang.annotation.RetentionPolicy;
  11 +import java.lang.annotation.Target;
  12 +
  13 +/**
  14 + * 滑动脱敏注解
  15 + *
  16 + * @author gaibu
  17 + */
  18 +@Documented
  19 +@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
  20 +@Retention(RetentionPolicy.RUNTIME)
  21 +@JacksonAnnotationsInside
  22 +@DesensitizeBy(handler = DefaultDesensitizationHandler.class)
  23 +public @interface SliderDesensitize {
  24 +
  25 + /**
  26 + * 后缀保留长度
  27 + */
  28 + int suffixKeep() default 0;
  29 +
  30 + /**
  31 + * 替换规则,会将前缀后缀保留后,全部替换成 replacer
  32 + *
  33 + * 例如:prefixKeep = 1; suffixKeep = 2; replacer = "*";
  34 + * 原始字符串 123456
  35 + * 脱敏后 1***56
  36 + */
  37 + String replacer() default "*";
  38 +
  39 + /**
  40 + * 前缀保留长度
  41 + */
  42 + int prefixKeep() default 0;
  43 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/AbstractSliderDesensitizationHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;
  4 +
  5 +import java.lang.annotation.Annotation;
  6 +
  7 +/**
  8 + * 滑动脱敏处理器抽象类,已实现通用的方法
  9 + *
  10 + * @author gaibu
  11 + */
  12 +public abstract class AbstractSliderDesensitizationHandler<T extends Annotation>
  13 + implements DesensitizationHandler<T> {
  14 +
  15 + @Override
  16 + public String desensitize(String origin, T annotation) {
  17 + int prefixKeep = getPrefixKeep(annotation);
  18 + int suffixKeep = getSuffixKeep(annotation);
  19 + String replacer = getReplacer(annotation);
  20 + int length = origin.length();
  21 +
  22 + // 情况一:原始字符串长度小于等于保留长度,则原始字符串全部替换
  23 + if (prefixKeep >= length || suffixKeep >= length) {
  24 + return buildReplacerByLength(replacer, length);
  25 + }
  26 +
  27 + // 情况二:原始字符串长度小于等于前后缀保留字符串长度,则原始字符串全部替换
  28 + if ((prefixKeep + suffixKeep) >= length) {
  29 + return buildReplacerByLength(replacer, length);
  30 + }
  31 +
  32 + // 情况三:原始字符串长度大于前后缀保留字符串长度,则替换中间字符串
  33 + int interval = length - prefixKeep - suffixKeep;
  34 + return origin.substring(0, prefixKeep) +
  35 + buildReplacerByLength(replacer, interval) +
  36 + origin.substring(prefixKeep + interval);
  37 + }
  38 +
  39 + /**
  40 + * 根据长度循环构建替换符
  41 + *
  42 + * @param replacer 替换符
  43 + * @param length 长度
  44 + * @return 构建后的替换符
  45 + */
  46 + private String buildReplacerByLength(String replacer, int length) {
  47 + StringBuilder builder = new StringBuilder();
  48 + for (int i = 0; i < length; i++) {
  49 + builder.append(replacer);
  50 + }
  51 + return builder.toString();
  52 + }
  53 +
  54 + /**
  55 + * 前缀保留长度
  56 + *
  57 + * @param annotation 注解信息
  58 + * @return 前缀保留长度
  59 + */
  60 + abstract Integer getPrefixKeep(T annotation);
  61 +
  62 + /**
  63 + * 后缀保留长度
  64 + *
  65 + * @param annotation 注解信息
  66 + * @return 后缀保留长度
  67 + */
  68 + abstract Integer getSuffixKeep(T annotation);
  69 +
  70 + /**
  71 + * 替换符
  72 + *
  73 + * @param annotation 注解信息
  74 + * @return 替换符
  75 + */
  76 + abstract String getReplacer(T annotation);
  77 +
  78 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/BankCardDesensitization.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.BankCardDesensitize;
  4 +
  5 +/**
  6 + * {@link BankCardDesensitize} 的脱敏处理器
  7 + *
  8 + * @author gaibu
  9 + */
  10 +public class BankCardDesensitization extends AbstractSliderDesensitizationHandler<BankCardDesensitize> {
  11 +
  12 + @Override
  13 + Integer getPrefixKeep(BankCardDesensitize annotation) {
  14 + return annotation.prefixKeep();
  15 + }
  16 +
  17 + @Override
  18 + Integer getSuffixKeep(BankCardDesensitize annotation) {
  19 + return annotation.suffixKeep();
  20 + }
  21 +
  22 + @Override
  23 + String getReplacer(BankCardDesensitize annotation) {
  24 + return annotation.replacer();
  25 + }
  26 +
  27 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/CarLicenseDesensitization.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.CarLicenseDesensitize;
  4 +
  5 +/**
  6 + * {@link CarLicenseDesensitize} 的脱敏处理器
  7 + *
  8 + * @author gaibu
  9 + */
  10 +public class CarLicenseDesensitization extends AbstractSliderDesensitizationHandler<CarLicenseDesensitize> {
  11 + @Override
  12 + Integer getPrefixKeep(CarLicenseDesensitize annotation) {
  13 + return annotation.prefixKeep();
  14 + }
  15 +
  16 + @Override
  17 + Integer getSuffixKeep(CarLicenseDesensitize annotation) {
  18 + return annotation.suffixKeep();
  19 + }
  20 +
  21 + @Override
  22 + String getReplacer(CarLicenseDesensitize annotation) {
  23 + return annotation.replacer();
  24 + }
  25 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/ChineseNameDesensitization.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.ChineseNameDesensitize;
  4 +
  5 +/**
  6 + * {@link ChineseNameDesensitize} 的脱敏处理器
  7 + *
  8 + * @author gaibu
  9 + */
  10 +public class ChineseNameDesensitization extends AbstractSliderDesensitizationHandler<ChineseNameDesensitize> {
  11 +
  12 + @Override
  13 + Integer getPrefixKeep(ChineseNameDesensitize annotation) {
  14 + return annotation.prefixKeep();
  15 + }
  16 +
  17 + @Override
  18 + Integer getSuffixKeep(ChineseNameDesensitize annotation) {
  19 + return annotation.suffixKeep();
  20 + }
  21 +
  22 + @Override
  23 + String getReplacer(ChineseNameDesensitize annotation) {
  24 + return annotation.replacer();
  25 + }
  26 +
  27 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/DefaultDesensitizationHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.SliderDesensitize;
  4 +
  5 +/**
  6 + * {@link SliderDesensitize} 的脱敏处理器
  7 + *
  8 + * @author gaibu
  9 + */
  10 +public class DefaultDesensitizationHandler extends AbstractSliderDesensitizationHandler<SliderDesensitize> {
  11 + @Override
  12 + Integer getPrefixKeep(SliderDesensitize annotation) {
  13 + return annotation.prefixKeep();
  14 + }
  15 +
  16 + @Override
  17 + Integer getSuffixKeep(SliderDesensitize annotation) {
  18 + return annotation.suffixKeep();
  19 + }
  20 +
  21 + @Override
  22 + String getReplacer(SliderDesensitize annotation) {
  23 + return annotation.replacer();
  24 + }
  25 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/FixedPhoneDesensitization.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.FixedPhoneDesensitize;
  4 +
  5 +/**
  6 + * {@link FixedPhoneDesensitize} 的脱敏处理器
  7 + *
  8 + * @author gaibu
  9 + */
  10 +public class FixedPhoneDesensitization extends AbstractSliderDesensitizationHandler<FixedPhoneDesensitize> {
  11 + @Override
  12 + Integer getPrefixKeep(FixedPhoneDesensitize annotation) {
  13 + return annotation.prefixKeep();
  14 + }
  15 +
  16 + @Override
  17 + Integer getSuffixKeep(FixedPhoneDesensitize annotation) {
  18 + return annotation.suffixKeep();
  19 + }
  20 +
  21 + @Override
  22 + String getReplacer(FixedPhoneDesensitize annotation) {
  23 + return annotation.replacer();
  24 + }
  25 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/IdCardDesensitization.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.IdCardDesensitize;
  4 +
  5 +/**
  6 + * {@link IdCardDesensitize} 的脱敏处理器
  7 + *
  8 + * @author gaibu
  9 + */
  10 +public class IdCardDesensitization extends AbstractSliderDesensitizationHandler<IdCardDesensitize> {
  11 + @Override
  12 + Integer getPrefixKeep(IdCardDesensitize annotation) {
  13 + return annotation.prefixKeep();
  14 + }
  15 +
  16 + @Override
  17 + Integer getSuffixKeep(IdCardDesensitize annotation) {
  18 + return annotation.suffixKeep();
  19 + }
  20 +
  21 + @Override
  22 + String getReplacer(IdCardDesensitize annotation) {
  23 + return annotation.replacer();
  24 + }
  25 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/MobileDesensitization.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.MobileDesensitize;
  4 +
  5 +/**
  6 + * {@link MobileDesensitize} 的脱敏处理器
  7 + *
  8 + * @author gaibu
  9 + */
  10 +public class MobileDesensitization extends AbstractSliderDesensitizationHandler<MobileDesensitize> {
  11 +
  12 + @Override
  13 + Integer getPrefixKeep(MobileDesensitize annotation) {
  14 + return annotation.prefixKeep();
  15 + }
  16 +
  17 + @Override
  18 + Integer getSuffixKeep(MobileDesensitize annotation) {
  19 + return annotation.suffixKeep();
  20 + }
  21 +
  22 + @Override
  23 + String getReplacer(MobileDesensitize annotation) {
  24 + return annotation.replacer();
  25 + }
  26 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/main/java/cn/iocoder/yudao/framework/desensitize/core/slider/handler/PasswordDesensitization.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.slider.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.PasswordDesensitize;
  4 +
  5 +/**
  6 + * {@link PasswordDesensitize} 的码脱敏处理器
  7 + *
  8 + * @author gaibu
  9 + */
  10 +public class PasswordDesensitization extends AbstractSliderDesensitizationHandler<PasswordDesensitize> {
  11 + @Override
  12 + Integer getPrefixKeep(PasswordDesensitize annotation) {
  13 + return annotation.prefixKeep();
  14 + }
  15 +
  16 + @Override
  17 + Integer getSuffixKeep(PasswordDesensitize annotation) {
  18 + return annotation.suffixKeep();
  19 + }
  20 +
  21 + @Override
  22 + String getReplacer(PasswordDesensitize annotation) {
  23 + return annotation.replacer();
  24 + }
  25 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/test/java/cn/iocoder/yudao/framework/desensitize/core/DesensitizeTest.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  4 +import cn.iocoder.yudao.framework.desensitize.core.regex.annotation.EmailDesensitize;
  5 +import cn.iocoder.yudao.framework.desensitize.core.regex.annotation.RegexDesensitize;
  6 +import cn.iocoder.yudao.framework.desensitize.core.annotation.Address;
  7 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.BankCardDesensitize;
  8 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.CarLicenseDesensitize;
  9 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.ChineseNameDesensitize;
  10 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.FixedPhoneDesensitize;
  11 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.IdCardDesensitize;
  12 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.PasswordDesensitize;
  13 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.MobileDesensitize;
  14 +import cn.iocoder.yudao.framework.desensitize.core.slider.annotation.SliderDesensitize;
  15 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  16 +import lombok.Data;
  17 +import org.junit.jupiter.api.Test;
  18 +
  19 +import static org.junit.jupiter.api.Assertions.*;
  20 +
  21 +/**
  22 + * {@link DesensitizeTest} 的单元测试
  23 + */
  24 +public class DesensitizeTest extends BaseMockitoUnitTest {
  25 +
  26 + @Test
  27 + public void test() {
  28 + // 准备参数
  29 + DesensitizeDemo desensitizeDemo = new DesensitizeDemo();
  30 + desensitizeDemo.setNickname("芋道源码");
  31 + desensitizeDemo.setBankCard("9988002866797031");
  32 + desensitizeDemo.setCarLicense("粤A66666");
  33 + desensitizeDemo.setFixedPhone("01086551122");
  34 + desensitizeDemo.setIdCard("530321199204074611");
  35 + desensitizeDemo.setPassword("123456");
  36 + desensitizeDemo.setPhoneNumber("13248765917");
  37 + desensitizeDemo.setSlider1("ABCDEFG");
  38 + desensitizeDemo.setSlider2("ABCDEFG");
  39 + desensitizeDemo.setSlider3("ABCDEFG");
  40 + desensitizeDemo.setEmail("1@email.com");
  41 + desensitizeDemo.setRegex("你好,我是芋道源码");
  42 + desensitizeDemo.setAddress("北京市海淀区上地十街10号");
  43 + desensitizeDemo.setOrigin("芋道源码");
  44 +
  45 + // 调用
  46 + DesensitizeDemo d = JsonUtils.parseObject(JsonUtils.toJsonString(desensitizeDemo), DesensitizeDemo.class);
  47 + // 断言
  48 + assertNotNull(d);
  49 + assertEquals("芋***", d.getNickname());
  50 + assertEquals("998800********31", d.getBankCard());
  51 + assertEquals("粤A6***6", d.getCarLicense());
  52 + assertEquals("0108*****22", d.getFixedPhone());
  53 + assertEquals("530321**********11", d.getIdCard());
  54 + assertEquals("******", d.getPassword());
  55 + assertEquals("132****5917", d.getPhoneNumber());
  56 + assertEquals("#######", d.getSlider1());
  57 + assertEquals("ABC*EFG", d.getSlider2());
  58 + assertEquals("*******", d.getSlider3());
  59 + assertEquals("1****@email.com", d.getEmail());
  60 + assertEquals("你好,我是*", d.getRegex());
  61 + assertEquals("北京市海淀区上地十街10号*", d.getAddress());
  62 + assertEquals("芋道源码", d.getOrigin());
  63 + }
  64 +
  65 + @Data
  66 + public static class DesensitizeDemo {
  67 +
  68 + @ChineseNameDesensitize
  69 + private String nickname;
  70 + @BankCardDesensitize
  71 + private String bankCard;
  72 + @CarLicenseDesensitize
  73 + private String carLicense;
  74 + @FixedPhoneDesensitize
  75 + private String fixedPhone;
  76 + @IdCardDesensitize
  77 + private String idCard;
  78 + @PasswordDesensitize
  79 + private String password;
  80 + @MobileDesensitize
  81 + private String phoneNumber;
  82 + @SliderDesensitize(prefixKeep = 6, suffixKeep = 1, replacer = "#")
  83 + private String slider1;
  84 + @SliderDesensitize(prefixKeep = 3, suffixKeep = 3)
  85 + private String slider2;
  86 + @SliderDesensitize(prefixKeep = 10)
  87 + private String slider3;
  88 + @EmailDesensitize
  89 + private String email;
  90 + @RegexDesensitize(regex = "芋道源码", replacer = "*")
  91 + private String regex;
  92 + @Address
  93 + private String address;
  94 + private String origin;
  95 +
  96 + }
  97 +
  98 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/test/java/cn/iocoder/yudao/framework/desensitize/core/annotation/Address.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.annotation;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.DesensitizeTest;
  4 +import cn.iocoder.yudao.framework.desensitize.core.base.annotation.DesensitizeBy;
  5 +import cn.iocoder.yudao.framework.desensitize.core.handler.AddressHandler;
  6 +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  7 +
  8 +import java.lang.annotation.Documented;
  9 +import java.lang.annotation.ElementType;
  10 +import java.lang.annotation.Retention;
  11 +import java.lang.annotation.RetentionPolicy;
  12 +import java.lang.annotation.Target;
  13 +
  14 +/**
  15 + * 地址
  16 + *
  17 + * 用于 {@link DesensitizeTest} 测试使用
  18 + *
  19 + * @author gaibu
  20 + */
  21 +@Documented
  22 +@Target({ElementType.FIELD})
  23 +@Retention(RetentionPolicy.RUNTIME)
  24 +@JacksonAnnotationsInside
  25 +@DesensitizeBy(handler = AddressHandler.class)
  26 +public @interface Address {
  27 +
  28 + String replacer() default "*";
  29 +
  30 +}
... ...
yudao-framework/yudao-spring-boot-starter-desensitize/src/test/java/cn/iocoder/yudao/framework/desensitize/core/handler/AddressHandler.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.desensitize.core.handler;
  2 +
  3 +import cn.iocoder.yudao.framework.desensitize.core.DesensitizeTest;
  4 +import cn.iocoder.yudao.framework.desensitize.core.base.handler.DesensitizationHandler;
  5 +import cn.iocoder.yudao.framework.desensitize.core.annotation.Address;
  6 +
  7 +/**
  8 + * {@link Address} 的脱敏处理器
  9 + *
  10 + * 用于 {@link DesensitizeTest} 测试使用
  11 + */
  12 +public class AddressHandler implements DesensitizationHandler<Address> {
  13 +
  14 + @Override
  15 + public String desensitize(String origin, Address annotation) {
  16 + return origin + annotation.replacer();
  17 + }
  18 +
  19 +}
... ...
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
... ... @@ -81,22 +81,19 @@ public interface BaseMapperX&lt;T&gt; extends BaseMapper&lt;T&gt; {
81 81 }
82 82  
83 83 /**
84   - * 逐条插入,适合少量数据插入,或者对性能要求不高的场景
85   - * <p>
86   - * 如果大量,请使用 {@link com.baomidou.mybatisplus.extension.service.impl.ServiceImpl#saveBatch(Collection)} 方法
87   - * 使用示例,可见 RoleMenuBatchInsertMapper、UserRoleBatchInsertMapper 类
  84 + * 批量插入,适合大量数据插入
88 85 *
89 86 * @param entities 实体们
90 87 */
91 88 default void insertBatch(Collection<T> entities) {
92   - entities.forEach(this::insert);
  89 + Db.saveBatch(entities);
93 90 }
94 91  
95 92 /**
96 93 * 批量插入,适合大量数据插入
97 94 *
98 95 * @param entities 实体们
99   - * @param size 插入数量 Db.saveBatch 默认为1000
  96 + * @param size 插入数量 Db.saveBatch 默认为 1000
100 97 */
101 98 default void insertBatch(Collection<T> entities, int size) {
102 99 Db.saveBatch(entities, size);
... ...
yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java
... ... @@ -7,7 +7,10 @@ import cn.hutool.core.util.StrUtil;
7 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
8 8 import uk.co.jemos.podam.api.PodamFactory;
9 9 import uk.co.jemos.podam.api.PodamFactoryImpl;
  10 +import uk.co.jemos.podam.common.AttributeStrategy;
10 11  
  12 +import javax.validation.constraints.Email;
  13 +import java.lang.annotation.Annotation;
11 14 import java.lang.reflect.Type;
12 15 import java.time.LocalDateTime;
13 16 import java.util.Arrays;
... ... @@ -95,6 +98,10 @@ public class RandomUtils {
95 98 return RandomUtil.randomEle(CommonStatusEnum.values()).getStatus();
96 99 }
97 100  
  101 + public static String randomEmail() {
  102 + return randomString() + "@qq.com";
  103 + }
  104 +
98 105 @SafeVarargs
99 106 public static <T> T randomPojo(Class<T> clazz, Consumer<T>... consumers) {
100 107 T pojo = PODAM_FACTORY.manufacturePojo(clazz);
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/package-info.java
  1 +/**
  2 + * Web 框架,全局异常、API 日志等
  3 + */
1 4 package cn.iocoder.yudao.framework;
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/YudaoWebAutoConfiguration.java
... ... @@ -2,27 +2,18 @@ package cn.iocoder.yudao.framework.web.config;
2 2  
3 3 import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
4 4 import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
5   -import cn.iocoder.yudao.framework.web.core.clean.JsoupXssCleaner;
6   -import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
7 5 import cn.iocoder.yudao.framework.web.core.filter.CacheRequestBodyFilter;
8 6 import cn.iocoder.yudao.framework.web.core.filter.DemoFilter;
9   -import cn.iocoder.yudao.framework.web.core.filter.XssFilter;
10 7 import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
11 8 import cn.iocoder.yudao.framework.web.core.handler.GlobalResponseBodyHandler;
12   -import cn.iocoder.yudao.framework.web.core.json.XssStringJsonDeserializer;
13 9 import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
14   -import com.fasterxml.jackson.databind.ObjectMapper;
15 10 import org.springframework.beans.factory.annotation.Value;
16 11 import org.springframework.boot.autoconfigure.AutoConfiguration;
17   -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
18   -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
19 12 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
20   -import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
21 13 import org.springframework.boot.context.properties.EnableConfigurationProperties;
22 14 import org.springframework.boot.web.servlet.FilterRegistrationBean;
23 15 import org.springframework.context.annotation.Bean;
24 16 import org.springframework.util.AntPathMatcher;
25   -import org.springframework.util.PathMatcher;
26 17 import org.springframework.web.bind.annotation.RestController;
27 18 import org.springframework.web.cors.CorsConfiguration;
28 19 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
... ... @@ -34,7 +25,7 @@ import javax.annotation.Resource;
34 25 import javax.servlet.Filter;
35 26  
36 27 @AutoConfiguration
37   -@EnableConfigurationProperties({WebProperties.class, XssProperties.class})
  28 +@EnableConfigurationProperties(WebProperties.class)
38 29 public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
39 30  
40 31 @Resource
... ... @@ -108,15 +99,6 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
108 99 }
109 100  
110 101 /**
111   - * 创建 XssFilter Bean,解决 Xss 安全问题
112   - */
113   - @Bean
114   - @ConditionalOnBean(XssCleaner.class)
115   - public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher, XssCleaner xssCleaner) {
116   - return createFilterBean(new XssFilter(properties, pathMatcher, xssCleaner), WebFilterOrderEnum.XSS_FILTER);
117   - }
118   -
119   - /**
120 102 * 创建 DemoFilter Bean,演示模式
121 103 */
122 104 @Bean
... ... @@ -125,33 +107,7 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
125 107 return createFilterBean(new DemoFilter(), WebFilterOrderEnum.DEMO_FILTER);
126 108 }
127 109  
128   -
129   - /**
130   - * Xss 清理者
131   - *
132   - * @return XssCleaner
133   - */
134   - @Bean
135   - @ConditionalOnMissingBean(XssCleaner.class)
136   - public XssCleaner xssCleaner() {
137   - return new JsoupXssCleaner();
138   - }
139   -
140   - /**
141   - * 注册 Jackson 的序列化器,用于处理 json 类型参数的 xss 过滤
142   - *
143   - * @return Jackson2ObjectMapperBuilderCustomizer
144   - */
145   - @Bean
146   - @ConditionalOnMissingBean(name = "xssJacksonCustomizer")
147   - @ConditionalOnBean(ObjectMapper.class)
148   - @ConditionalOnProperty(value = "yudao.xss.enable", havingValue = "true")
149   - public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssCleaner xssCleaner) {
150   - // 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer,在序列化时进行处理
151   - return builder -> builder.deserializerByType(String.class, new XssStringJsonDeserializer(xssCleaner));
152   - }
153   -
154   - private static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
  110 + public static <T extends Filter> FilterRegistrationBean<T> createFilterBean(T filter, Integer order) {
155 111 FilterRegistrationBean<T> bean = new FilterRegistrationBean<>(filter);
156 112 bean.setOrder(order);
157 113 return bean;
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/config/XssProperties.java renamed to yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/XssProperties.java
1   -package cn.iocoder.yudao.framework.web.config;
  1 +package cn.iocoder.yudao.framework.xss.config;
2 2  
3 3 import lombok.Data;
4 4 import org.springframework.boot.context.properties.ConfigurationProperties;
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.xss.config;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
  4 +import cn.iocoder.yudao.framework.xss.core.clean.JsoupXssCleaner;
  5 +import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
  6 +import cn.iocoder.yudao.framework.xss.core.filter.XssFilter;
  7 +import cn.iocoder.yudao.framework.xss.core.json.XssStringJsonDeserializer;
  8 +import com.fasterxml.jackson.databind.ObjectMapper;
  9 +import org.springframework.boot.autoconfigure.AutoConfiguration;
  10 +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
  11 +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  12 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  13 +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
  14 +import org.springframework.boot.context.properties.EnableConfigurationProperties;
  15 +import org.springframework.boot.web.servlet.FilterRegistrationBean;
  16 +import org.springframework.context.annotation.Bean;
  17 +import org.springframework.util.PathMatcher;
  18 +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  19 +
  20 +import static cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration.createFilterBean;
  21 +
  22 +@AutoConfiguration
  23 +@EnableConfigurationProperties(XssProperties.class)
  24 +public class YudaoXssAutoConfiguration implements WebMvcConfigurer {
  25 +
  26 + /**
  27 + * Xss 清理者
  28 + *
  29 + * @return XssCleaner
  30 + */
  31 + @Bean
  32 + @ConditionalOnMissingBean(XssCleaner.class)
  33 + public XssCleaner xssCleaner() {
  34 + return new JsoupXssCleaner();
  35 + }
  36 +
  37 + /**
  38 + * 注册 Jackson 的序列化器,用于处理 json 类型参数的 xss 过滤
  39 + *
  40 + * @return Jackson2ObjectMapperBuilderCustomizer
  41 + */
  42 + @Bean
  43 + @ConditionalOnMissingBean(name = "xssJacksonCustomizer")
  44 + @ConditionalOnBean(ObjectMapper.class)
  45 + @ConditionalOnProperty(value = "yudao.xss.enable", havingValue = "true")
  46 + public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssCleaner xssCleaner) {
  47 + // 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer,在序列化时进行处理
  48 + return builder -> builder.deserializerByType(String.class, new XssStringJsonDeserializer(xssCleaner));
  49 + }
  50 +
  51 + /**
  52 + * 创建 XssFilter Bean,解决 Xss 安全问题
  53 + */
  54 + @Bean
  55 + @ConditionalOnBean(XssCleaner.class)
  56 + public FilterRegistrationBean<XssFilter> xssFilter(XssProperties properties, PathMatcher pathMatcher, XssCleaner xssCleaner) {
  57 + return createFilterBean(new XssFilter(properties, pathMatcher, xssCleaner), WebFilterOrderEnum.XSS_FILTER);
  58 + }
  59 +
  60 +}
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/clean/JsoupXssCleaner.java renamed to yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/clean/JsoupXssCleaner.java
1   -package cn.iocoder.yudao.framework.web.core.clean;
  1 +package cn.iocoder.yudao.framework.xss.core.clean;
2 2  
3 3 import org.jsoup.Jsoup;
4 4 import org.jsoup.nodes.Document;
5 5 import org.jsoup.safety.Safelist;
6 6  
7 7 /**
8   - * jsonp 过滤字符串
  8 + * 基于 JSONP 实现 XSS 过滤字符串
9 9 */
10 10 public class JsoupXssCleaner implements XssCleaner {
11 11  
... ... @@ -24,21 +24,6 @@ public class JsoupXssCleaner implements XssCleaner {
24 24 this.baseUri = "";
25 25 }
26 26  
27   - public JsoupXssCleaner(Safelist safelist) {
28   - this.safelist = safelist;
29   - this.baseUri = "";
30   - }
31   -
32   - public JsoupXssCleaner(String baseUri) {
33   - this.safelist = buildSafelist();
34   - this.baseUri = baseUri;
35   - }
36   -
37   - public JsoupXssCleaner(Safelist safelist, String baseUri) {
38   - this.safelist = safelist;
39   - this.baseUri = baseUri;
40   - }
41   -
42 27 /**
43 28 * 构建一个 Xss 清理的 Safelist 规则。
44 29 * 基于 Safelist#relaxed() 的基础上:
... ... @@ -67,7 +52,6 @@ public class JsoupXssCleaner implements XssCleaner {
67 52 // 虽然可以重写 WhiteList#isSafeAttribute 来处理,但是有隐患,所以暂时不支持相对路径
68 53 // WHITELIST.removeProtocols("a", "href", "ftp", "http", "https", "mailto");
69 54 // WHITELIST.removeProtocols("img", "src", "http", "https");
70   -
71 55 return relaxedSafelist;
72 56 }
73 57  
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/clean/XssCleaner.java renamed to yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/clean/XssCleaner.java
1   -package cn.iocoder.yudao.framework.web.core.clean;
  1 +package cn.iocoder.yudao.framework.xss.core.clean;
2 2  
3 3 /**
4 4 * 对 html 文本中的有 Xss 风险的数据进行清理
... ... @@ -12,4 +12,5 @@ public interface XssCleaner {
12 12 * @return 清理后的 html
13 13 */
14 14 String clean(String html);
  15 +
15 16 }
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/XssFilter.java renamed to yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/filter/XssFilter.java
1   -package cn.iocoder.yudao.framework.web.core.filter;
  1 +package cn.iocoder.yudao.framework.xss.core.filter;
2 2  
3   -import cn.iocoder.yudao.framework.web.config.XssProperties;
4   -import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
  3 +import cn.iocoder.yudao.framework.xss.config.XssProperties;
  4 +import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
5 5 import lombok.AllArgsConstructor;
6 6 import org.springframework.util.PathMatcher;
7 7 import org.springframework.web.filter.OncePerRequestFilter;
... ... @@ -14,8 +14,6 @@ import java.io.IOException;
14 14  
15 15 /**
16 16 * Xss 过滤器
17   - * <p>
18   - * 对 Xss 不了解的胖友,可以看看 http://www.iocoder.cn/Fight/The-new-girl-asked-me-why-AJAX-requests-are-not-secure-I-did-not-answer/
19 17 *
20 18 * @author 芋道源码
21 19 */
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/XssRequestWrapper.java renamed to yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/filter/XssRequestWrapper.java
1   -package cn.iocoder.yudao.framework.web.core.filter;
  1 +package cn.iocoder.yudao.framework.xss.core.filter;
2 2  
3   -import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
  3 +import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
4 4  
5 5 import javax.servlet.http.HttpServletRequest;
6 6 import javax.servlet.http.HttpServletRequestWrapper;
... ... @@ -13,6 +13,7 @@ import java.util.Map;
13 13 * @author 芋道源码
14 14 */
15 15 public class XssRequestWrapper extends HttpServletRequestWrapper {
  16 +
16 17 private final XssCleaner xssCleaner;
17 18  
18 19 public XssRequestWrapper(HttpServletRequest request, XssCleaner xssCleaner) {
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/json/XssStringJsonDeserializer.java renamed to yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/core/json/XssStringJsonDeserializer.java
1   -package cn.iocoder.yudao.framework.web.core.json;
  1 +package cn.iocoder.yudao.framework.xss.core.json;
2 2  
3   -import cn.iocoder.yudao.framework.web.core.clean.XssCleaner;
  3 +import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
4 4 import com.fasterxml.jackson.core.JsonParser;
5 5 import com.fasterxml.jackson.core.JsonToken;
6 6 import com.fasterxml.jackson.databind.DeserializationContext;
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/package-info.java 0 → 100644
  1 +/**
  2 + * 针对 XSS 的基础封装
  3 + *
  4 + * XSS 说明:https://tech.meituan.com/2018/09/27/fe-security.html
  5 + */
  6 +package cn.iocoder.yudao.framework.xss;
... ...
yudao-framework/yudao-spring-boot-starter-web/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
... ... @@ -2,4 +2,5 @@ cn.iocoder.yudao.framework.apilog.config.YudaoApiLogAutoConfiguration
2 2 cn.iocoder.yudao.framework.jackson.config.YudaoJacksonAutoConfiguration
3 3 com.github.xiaoymin.knife4j.spring.configuration.Knife4jAutoConfiguration
4 4 cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration
5   -cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration
6 5 \ No newline at end of file
  6 +cn.iocoder.yudao.framework.web.config.YudaoWebAutoConfiguration
  7 +cn.iocoder.yudao.framework.xss.config.YudaoXssAutoConfiguration
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java
... ... @@ -6,15 +6,7 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 6 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.config.FileConfigPageReqVO;
7 7 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9   -import org.apache.ibatis.annotations.Select;
10 9  
11   -import java.time.LocalDateTime;
12   -
13   -/**
14   - * 文件配置 Mapper
15   - *
16   - * @author 芋道源码
17   - */
18 10 @Mapper
19 11 public interface FileConfigMapper extends BaseMapperX<FileConfigDO> {
20 12  
... ... @@ -26,7 +18,4 @@ public interface FileConfigMapper extends BaseMapperX&lt;FileConfigDO&gt; {
26 18 .orderByDesc(FileConfigDO::getId));
27 19 }
28 20  
29   - @Select("SELECT COUNT(*) FROM infra_file_config WHERE update_time > #{maxUpdateTime}")
30   - Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
31   -
32 21 }
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/mq/consumer/file/FileConfigRefreshConsumer.java
... ... @@ -23,7 +23,7 @@ public class FileConfigRefreshConsumer extends AbstractChannelMessageListener&lt;Fi
23 23 @Override
24 24 public void onMessage(FileConfigRefreshMessage message) {
25 25 log.info("[onMessage][收到 FileConfig 刷新消息]");
26   - fileConfigService.initFileClients();
  26 + fileConfigService.initLocalCache();
27 27 }
28 28  
29 29 }
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/CodegenServiceImpl.java
... ... @@ -237,10 +237,6 @@ public class CodegenServiceImpl implements CodegenService {
237 237 @Override
238 238 public List<DatabaseTableRespVO> getDatabaseTableList(Long dataSourceConfigId, String name, String comment) {
239 239 List<TableInfo> tables = databaseTableService.getTableList(dataSourceConfigId, name, comment);
240   - // 移除置顶前缀的表名 // TODO 未来做成可配置
241   - tables.removeIf(table -> table.getName().toUpperCase().startsWith("QRTZ_"));
242   - tables.removeIf(table -> table.getName().toUpperCase().startsWith("ACT_"));
243   - tables.removeIf(table -> table.getName().toUpperCase().startsWith("FLW_"));
244 240 // 移除已经生成的表
245 241 // 移除在 Codegen 中,已经存在的
246 242 Set<String> existsTables = CollectionUtils.convertSet(
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImpl.java
... ... @@ -52,7 +52,11 @@ public class DatabaseTableServiceImpl implements DatabaseTableService {
52 52 StrategyConfig.Builder strategyConfig = new StrategyConfig.Builder();
53 53 if (StrUtil.isNotEmpty(name)) {
54 54 strategyConfig.addInclude(name);
  55 + } else {
  56 + // 移除工作流和定时任务前缀的表名 // TODO 未来做成可配置
  57 + strategyConfig.addExclude("ACT_[\\S\\s]+|QRTZ_[\\S\\s]+|FLW_[\\S\\s]+");
55 58 }
  59 +
56 60 GlobalConfig globalConfig = new GlobalConfig.Builder().dateType(DateType.TIME_PACK).build(); // 只使用 Date 类型,不使用 LocalDate
57 61 ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, strategyConfig.build(),
58 62 null, globalConfig, null);
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java
... ... @@ -21,7 +21,7 @@ public interface FileConfigService {
21 21 /**
22 22 * 初始化文件客户端
23 23 */
24   - void initFileClients();
  24 + void initLocalCache();
25 25  
26 26 /**
27 27 * 创建文件配置
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java
... ... @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.infra.service.file;
3 3 import cn.hutool.core.io.resource.ResourceUtil;
4 4 import cn.hutool.core.util.IdUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7 6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
8 7 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
9 8 import cn.iocoder.yudao.framework.file.core.client.FileClient;
... ... @@ -19,7 +18,6 @@ import cn.iocoder.yudao.module.infra.dal.mysql.file.FileConfigMapper;
19 18 import cn.iocoder.yudao.module.infra.mq.producer.file.FileConfigProducer;
20 19 import lombok.Getter;
21 20 import lombok.extern.slf4j.Slf4j;
22   -import org.springframework.scheduling.annotation.Scheduled;
23 21 import org.springframework.stereotype.Service;
24 22 import org.springframework.transaction.annotation.Transactional;
25 23 import org.springframework.transaction.support.TransactionSynchronization;
... ... @@ -29,7 +27,6 @@ import org.springframework.validation.annotation.Validated;
29 27 import javax.annotation.PostConstruct;
30 28 import javax.annotation.Resource;
31 29 import javax.validation.Validator;
32   -import java.time.LocalDateTime;
33 30 import java.util.Collection;
34 31 import java.util.List;
35 32 import java.util.Map;
... ... @@ -48,18 +45,6 @@ import static cn.iocoder.yudao.module.infra.enums.ErrorCodeConstants.FILE_CONFIG
48 45 @Slf4j
49 46 public class FileConfigServiceImpl implements FileConfigService {
50 47  
51   - /**
52   - * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
53   - * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
54   - */
55   - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
56   -
57   - /**
58   - * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
59   - */
60   - @Getter
61   - private volatile LocalDateTime maxUpdateTime;
62   -
63 48 @Resource
64 49 private FileClientFactory fileClientFactory;
65 50 /**
... ... @@ -79,34 +64,12 @@ public class FileConfigServiceImpl implements FileConfigService {
79 64  
80 65 @Override
81 66 @PostConstruct
82   - public void initFileClients() {
83   - initLocalCacheIfUpdate(null);
84   - }
85   -
86   - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
87   - public void schedulePeriodicRefresh() {
88   - initLocalCacheIfUpdate(this.maxUpdateTime);
89   - }
90   -
91   - /**
92   - * 刷新本地缓存
93   - *
94   - * @param maxUpdateTime 最大更新时间
95   - * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
96   - * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
97   - */
98   - private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
99   - // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
100   - // 如果没有增量的数据变化,则不进行本地缓存的刷新
101   - if (maxUpdateTime != null
102   - && fileConfigMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
103   - log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
104   - return;
105   - }
  67 + public void initLocalCache() {
  68 + // 第一步:查询数据
106 69 List<FileConfigDO> configs = fileConfigMapper.selectList();
107   - log.info("[initLocalCacheIfUpdate][缓存文件配置,数量为:{}]", configs.size());
  70 + log.info("[initLocalCache][缓存文件配置,数量为:{}]", configs.size());
108 71  
109   - // 第二步:构建缓存。创建或更新文件 Client
  72 + // 第二步:构建缓存:创建或更新文件 Client
110 73 configs.forEach(config -> {
111 74 fileClientFactory.createOrUpdateFileClient(config.getId(), config.getStorage(), config.getConfig());
112 75 // 如果是 master,进行设置
... ... @@ -114,9 +77,6 @@ public class FileConfigServiceImpl implements FileConfigService {
114 77 masterFileClient = fileClientFactory.getFileClient(config.getId());
115 78 }
116 79 });
117   -
118   - // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
119   - this.maxUpdateTime = CollectionUtils.getMaxValue(configs, FileConfigDO::getUpdateTime);
120 80 }
121 81  
122 82 @Override
... ...
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/test/TestDemoServiceImpl.java
... ... @@ -79,7 +79,7 @@ public class TestDemoServiceImpl implements TestDemoService {
79 79  
80 80 @Override
81 81 public PageResult<TestDemoDO> getTestDemoPage(TestDemoPageReqVO pageReqVO) {
82   -// testDemoMapper.selectList2();
  82 + testDemoMapper.selectList2();
83 83 return testDemoMapper.selectPage(pageReqVO);
84 84 }
85 85  
... ...
yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java
... ... @@ -30,7 +30,6 @@ import java.util.Map;
30 30 import static cn.hutool.core.util.RandomUtil.randomEle;
31 31 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
32 32 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
33   -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
34 33 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
35 34 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
36 35 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
... ... @@ -74,16 +73,13 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
74 73 when(fileClientFactory.getFileClient(eq(1L))).thenReturn(masterFileClient);
75 74  
76 75 // 调用
77   - fileConfigService.initFileClients();
  76 + fileConfigService.initLocalCache();
78 77 // 断言 fileClientFactory 调用
79 78 verify(fileClientFactory).createOrUpdateFileClient(eq(1L),
80 79 eq(configDO1.getStorage()), eq(configDO1.getConfig()));
81 80 verify(fileClientFactory).createOrUpdateFileClient(eq(2L),
82 81 eq(configDO2.getStorage()), eq(configDO2.getConfig()));
83 82 assertSame(masterFileClient, fileConfigService.getMasterFileClient());
84   - // 断言 maxUpdateTime 缓存
85   - assertEquals(max(configDO1.getUpdateTime(), configDO2.getUpdateTime()),
86   - fileConfigService.getMaxUpdateTime());
87 83 }
88 84  
89 85 @Test
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/merchant/PayChannelMapper.java
... ... @@ -8,9 +8,7 @@ import cn.iocoder.yudao.module.pay.controller.admin.merchant.vo.channel.PayChann
8 8 import cn.iocoder.yudao.module.pay.dal.dataobject.merchant.PayChannelDO;
9 9 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
10 10 import org.apache.ibatis.annotations.Mapper;
11   -import org.apache.ibatis.annotations.Select;
12 11  
13   -import java.time.LocalDateTime;
14 12 import java.util.Collection;
15 13 import java.util.List;
16 14  
... ... @@ -21,9 +19,6 @@ public interface PayChannelMapper extends BaseMapperX&lt;PayChannelDO&gt; {
21 19 return selectOne(PayChannelDO::getAppId, appId, PayChannelDO::getCode, code);
22 20 }
23 21  
24   - @Select("SELECT COUNT(*) FROM pay_channel WHERE update_time > #{maxUpdateTime}")
25   - Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
26   -
27 22 default PageResult<PayChannelDO> selectPage(PayChannelPageReqVO reqVO) {
28 23 return selectPage(reqVO, new QueryWrapperX<PayChannelDO>()
29 24 .eqIfPresent("code", reqVO.getCode())
... ... @@ -67,14 +62,14 @@ public interface PayChannelMapper extends BaseMapperX&lt;PayChannelDO&gt; {
67 62 * 根据条件获取渠道
68 63 *
69 64 * @param merchantId 商户编号
70   - * @param appid 应用编号 // TODO @aquan:appid =》appId
  65 + * @param appI 应用编号
71 66 * @param code 渠道编码
72 67 * @return 数量
73 68 */
74   - default PayChannelDO selectOne(Long merchantId, Long appid, String code) {
  69 + default PayChannelDO selectOne(Long merchantId, Long appI, String code) {
75 70 return this.selectOne((new QueryWrapper<PayChannelDO>().lambda()
76 71 .eq(PayChannelDO::getMerchantId, merchantId)
77   - .eq(PayChannelDO::getAppId, appid)
  72 + .eq(PayChannelDO::getAppId, appI)
78 73 .eq(PayChannelDO::getCode, code)
79 74 ));
80 75 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/merchant/PayChannelServiceImpl.java
... ... @@ -5,7 +5,6 @@ import cn.hutool.json.JSONUtil;
5 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 6 import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
7 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
8   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
9 8 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
10 9 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
11 10 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
... ... @@ -19,7 +18,6 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.merchant.PayChannelDO;
19 18 import cn.iocoder.yudao.module.pay.dal.mysql.merchant.PayChannelMapper;
20 19 import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;
21 20 import lombok.extern.slf4j.Slf4j;
22   -import org.springframework.scheduling.annotation.Scheduled;
23 21 import org.springframework.stereotype.Service;
24 22 import org.springframework.validation.annotation.Validated;
25 23  
... ... @@ -45,12 +43,6 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.CHANNEL_NOT_E
45 43 public class PayChannelServiceImpl implements PayChannelService {
46 44  
47 45 /**
48   - * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
49   - * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
50   - */
51   - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
52   -
53   - /**
54 46 * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
55 47 */
56 48 private volatile LocalDateTime maxUpdateTime;
... ... @@ -70,40 +62,15 @@ public class PayChannelServiceImpl implements PayChannelService {
70 62 @Override
71 63 @PostConstruct
72 64 public void initLocalCache() {
73   - initLocalCacheIfUpdate(null);
74   - }
75   -
76   - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
77   - public void schedulePeriodicRefresh() {
78   - initLocalCacheIfUpdate(this.maxUpdateTime);
79   - }
80   -
81   - /**
82   - * 刷新本地缓存
83   - *
84   - * @param maxUpdateTime 最大更新时间
85   - * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
86   - * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
87   - */
88   - private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
89 65 // 注意:忽略自动多租户,因为要全局初始化缓存
90 66 TenantUtils.executeIgnore(() -> {
91   - // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
92   - // 如果没有增量的数据变化,则不进行本地缓存的刷新
93   - if (maxUpdateTime != null
94   - && channelMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
95   - log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
96   - return;
97   - }
  67 + // 第一步:查询数据
98 68 List<PayChannelDO> channels = channelMapper.selectList();
99   - log.info("[initLocalCacheIfUpdate][缓存支付渠道,数量为:{}]", channels.size());
  69 + log.info("[initLocalCache][缓存支付渠道,数量为:{}]", channels.size());
100 70  
101   - // 第二步:构建缓存。创建或更新支付 Client
  71 + // 第二步:构建缓存:创建或更新支付 Client
102 72 channels.forEach(payChannel -> payClientFactory.createOrUpdatePayClient(payChannel.getId(),
103 73 payChannel.getCode(), payChannel.getConfig()));
104   -
105   - // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
106   - this.maxUpdateTime = CollectionUtils.getMaxValue(channels, PayChannelDO::getUpdateTime);
107 74 });
108 75 }
109 76  
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApi.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.mail;
  2 +
  3 +import cn.iocoder.yudao.module.system.api.mail.dto.MailSendSingleToUserReqDTO;
  4 +
  5 +import javax.validation.Valid;
  6 +
  7 +/**
  8 + * 邮箱发送 API 接口
  9 + *
  10 + * @author 芋道源码
  11 + */
  12 +public interface MailSendApi {
  13 +
  14 + /**
  15 + * 发送单条邮箱给 Admin 用户
  16 + *
  17 + * 在 mail 为空时,使用 userId 加载对应 Admin 的邮箱
  18 + *
  19 + * @param reqDTO 发送请求
  20 + * @return 发送日志编号
  21 + */
  22 + Long sendSingleMailToAdmin(@Valid MailSendSingleToUserReqDTO reqDTO);
  23 +
  24 + /**
  25 + * 发送单条邮箱给 Member 用户
  26 + *
  27 + * 在 mail 为空时,使用 userId 加载对应 Member 的邮箱
  28 + *
  29 + * @param reqDTO 发送请求
  30 + * @return 发送日志编号
  31 + */
  32 + Long sendSingleMailToMember(@Valid MailSendSingleToUserReqDTO reqDTO);
  33 +
  34 +}
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/mail/dto/MailSendSingleToUserReqDTO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.mail.dto;
  2 +
  3 +import lombok.Data;
  4 +
  5 +import javax.validation.constraints.Email;
  6 +import javax.validation.constraints.NotNull;
  7 +import java.util.Map;
  8 +
  9 +/**
  10 + * 邮件发送 Request DTO
  11 + *
  12 + * @author wangjingqi
  13 + */
  14 +@Data
  15 +public class MailSendSingleToUserReqDTO {
  16 +
  17 + /**
  18 + * 用户编号
  19 + */
  20 + private Long userId;
  21 + /**
  22 + * 邮箱
  23 + */
  24 + @Email
  25 + private String mail;
  26 +
  27 + /**
  28 + * 邮件模板编号
  29 + */
  30 + @NotNull(message = "邮件模板编号不能为空")
  31 + private String templateCode;
  32 + /**
  33 + * 邮件模板参数
  34 + */
  35 + private Map<String, Object> templateParams;
  36 +
  37 +}
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
... ... @@ -141,4 +141,16 @@ public interface ErrorCodeConstants {
141 141 ErrorCode OAUTH2_CODE_NOT_EXISTS = new ErrorCode(1002022000, "code 不存在");
142 142 ErrorCode OAUTH2_CODE_EXPIRE = new ErrorCode(1002022001, "code 已过期");
143 143  
  144 + // ========== 邮箱账号 1002023000 ==========
  145 + ErrorCode MAIL_ACCOUNT_NOT_EXISTS = new ErrorCode(1002023000, "邮箱账号不存在");
  146 + ErrorCode MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS = new ErrorCode(1002023001, "无法删除,该邮箱账号还有邮件模板");
  147 +
  148 + // ========== 邮件模版 1002024000 ==========
  149 + ErrorCode MAIL_TEMPLATE_NOT_EXISTS = new ErrorCode(1002024000, "邮件模版不存在");
  150 + ErrorCode MAIL_TEMPLATE_CODE_EXISTS = new ErrorCode(1002024001, "邮件模版 code({}) 已存在");
  151 +
  152 + // ========== 邮件发送 1002025000 ==========
  153 + ErrorCode MAIL_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1002025000, "模板参数({})缺失");
  154 + ErrorCode MAIL_SEND_MAIL_NOT_EXISTS = new ErrorCode(1002025000, "邮箱不存在");
  155 +
144 156 }
... ...
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/mail/MailSendStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.enums.mail;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 邮件的发送状态枚举
  8 + *
  9 + * @author wangjingyi
  10 + * @since 2022/4/10 13:39
  11 + */
  12 +@Getter
  13 +@AllArgsConstructor
  14 +public enum MailSendStatusEnum {
  15 +
  16 + INIT(0), // 初始化
  17 + SUCCESS(10), // 发送成功
  18 + FAILURE(20), // 发送失败
  19 + IGNORE(30), // 忽略,即不发送
  20 + ;
  21 +
  22 + private final int status;
  23 +
  24 +}
... ...
yudao-module-system/yudao-module-system-biz/pom.xml
... ... @@ -111,6 +111,10 @@
111 111 <artifactId>yudao-spring-boot-starter-captcha</artifactId>
112 112 </dependency>
113 113  
  114 + <dependency>
  115 + <groupId>org.springframework.boot</groupId>
  116 + <artifactId>spring-boot-starter-mail</artifactId>
  117 + </dependency>
114 118 </dependencies>
115 119  
116 120 </project>
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/mail/MailSendApiImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.api.mail;
  2 +
  3 +import cn.iocoder.yudao.module.system.api.mail.dto.MailSendSingleToUserReqDTO;
  4 +import cn.iocoder.yudao.module.system.service.mail.MailSendService;
  5 +import org.springframework.stereotype.Service;
  6 +import org.springframework.validation.annotation.Validated;
  7 +
  8 +import javax.annotation.Resource;
  9 +
  10 +/**
  11 + * 邮件发送 API 实现类
  12 + *
  13 + * @author wangjingyi
  14 + */
  15 +@Service
  16 +@Validated
  17 +public class MailSendApiImpl implements MailSendApi {
  18 +
  19 + @Resource
  20 + private MailSendService mailSendService;
  21 +
  22 + @Override
  23 + public Long sendSingleMailToAdmin(MailSendSingleToUserReqDTO reqDTO) {
  24 + return mailSendService.sendSingleMailToAdmin(reqDTO.getMail(), reqDTO.getUserId(),
  25 + reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
  26 + }
  27 +
  28 + @Override
  29 + public Long sendSingleMailToMember(MailSendSingleToUserReqDTO reqDTO) {
  30 + return mailSendService.sendSingleMailToMember(reqDTO.getMail(), reqDTO.getUserId(),
  31 + reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
  32 + }
  33 +
  34 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailAccountController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail;
  2 +
  3 +
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.*;
  7 +import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  9 +import cn.iocoder.yudao.module.system.service.mail.MailAccountService;
  10 +import io.swagger.annotations.Api;
  11 +import io.swagger.annotations.ApiImplicitParam;
  12 +import io.swagger.annotations.ApiOperation;
  13 +import org.springframework.security.access.prepost.PreAuthorize;
  14 +import org.springframework.web.bind.annotation.*;
  15 +
  16 +import javax.annotation.Resource;
  17 +import javax.validation.Valid;
  18 +import java.util.Comparator;
  19 +import java.util.List;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +
  23 +@Api(tags = "管理后台 - 邮箱账号")
  24 +@RestController
  25 +@RequestMapping("/system/mail-account")
  26 +public class MailAccountController {
  27 +
  28 + @Resource
  29 + private MailAccountService mailAccountService;
  30 +
  31 + @PostMapping("/create")
  32 + @ApiOperation("创建邮箱账号")
  33 + @PreAuthorize("@ss.hasPermission('system:mail-account:create')")
  34 + public CommonResult<Long> createMailAccount(@Valid @RequestBody MailAccountCreateReqVO createReqVO) {
  35 + return success(mailAccountService.createMailAccount(createReqVO));
  36 + }
  37 +
  38 + @PutMapping("/update")
  39 + @ApiOperation("修改邮箱账号")
  40 + @PreAuthorize("@ss.hasPermission('system:mail-account:update')")
  41 + public CommonResult<Boolean> updateMailAccount(@Valid @RequestBody MailAccountUpdateReqVO updateReqVO) {
  42 + mailAccountService.updateMailAccount(updateReqVO);
  43 + return success(true);
  44 + }
  45 +
  46 + @DeleteMapping("/delete")
  47 + @ApiOperation("删除邮箱账号")
  48 + @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
  49 + @PreAuthorize("@ss.hasPermission('system:mail-account:delete')")
  50 + public CommonResult<Boolean> deleteMailAccount(@RequestParam Long id) {
  51 + mailAccountService.deleteMailAccount(id);
  52 + return success(true);
  53 + }
  54 +
  55 + @GetMapping("/get")
  56 + @ApiOperation("获得邮箱账号")
  57 + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
  58 + @PreAuthorize("@ss.hasPermission('system:mail-account:get')")
  59 + public CommonResult<MailAccountRespVO> getMailAccount(@RequestParam("id") Long id) {
  60 + MailAccountDO mailAccountDO = mailAccountService.getMailAccount(id);
  61 + return success(MailAccountConvert.INSTANCE.convert(mailAccountDO));
  62 + }
  63 +
  64 + @GetMapping("/page")
  65 + @ApiOperation("获得邮箱账号分页")
  66 + @PreAuthorize("@ss.hasPermission('system:mail-account:query')")
  67 + public CommonResult<PageResult<MailAccountBaseVO>> getMailAccountPage(@Valid MailAccountPageReqVO pageReqVO) {
  68 + PageResult<MailAccountDO> pageResult = mailAccountService.getMailAccountPage(pageReqVO);
  69 + return success(MailAccountConvert.INSTANCE.convertPage(pageResult));
  70 + }
  71 +
  72 + @GetMapping("/list-all-simple")
  73 + @ApiOperation(value = "获得邮箱账号精简列表")
  74 + public CommonResult<List<MailAccountSimpleRespVO>> getSimpleMailAccountList() {
  75 + List<MailAccountDO> list = mailAccountService.getMailAccountList();
  76 + return success(MailAccountConvert.INSTANCE.convertList02(list));
  77 + }
  78 +
  79 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailLogController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail;
  2 +
  3 +
  4 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
  7 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogRespVO;
  8 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateRespVO;
  9 +import cn.iocoder.yudao.module.system.convert.mail.MailLogConvert;
  10 +import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert;
  11 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
  12 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  13 +import cn.iocoder.yudao.module.system.service.mail.MailLogService;
  14 +import io.swagger.annotations.Api;
  15 +import io.swagger.annotations.ApiImplicitParam;
  16 +import io.swagger.annotations.ApiOperation;
  17 +import org.springframework.security.access.prepost.PreAuthorize;
  18 +import org.springframework.web.bind.annotation.GetMapping;
  19 +import org.springframework.web.bind.annotation.RequestMapping;
  20 +import org.springframework.web.bind.annotation.RequestParam;
  21 +import org.springframework.web.bind.annotation.RestController;
  22 +
  23 +import javax.annotation.Resource;
  24 +import javax.validation.Valid;
  25 +
  26 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  27 +
  28 +
  29 +@Api(tags = "管理后台 - 邮件日志")
  30 +@RestController
  31 +@RequestMapping("/system/mail-log")
  32 +public class MailLogController {
  33 +
  34 + @Resource
  35 + private MailLogService mailLogService;
  36 +
  37 + @GetMapping("/page")
  38 + @ApiOperation("获得邮箱日志分页")
  39 + @PreAuthorize("@ss.hasPermission('system:mail-log:query')")
  40 + public CommonResult<PageResult<MailLogRespVO>> getMailLogPage(@Valid MailLogPageReqVO pageVO) {
  41 + PageResult<MailLogDO> pageResult = mailLogService.getMailLogPage(pageVO);
  42 + return success(MailLogConvert.INSTANCE.convertPage(pageResult));
  43 + }
  44 +
  45 + @GetMapping("/get")
  46 + @ApiOperation("获得邮箱日志")
  47 + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
  48 + @PreAuthorize("@ss.hasPermission('system:mail-log:query')")
  49 + public CommonResult<MailLogRespVO> getMailTemplate(@RequestParam("id") Long id) {
  50 + MailLogDO mailLogDO = mailLogService.getMailLog(id);
  51 + return success(MailLogConvert.INSTANCE.convert(mailLogDO));
  52 + }
  53 +
  54 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.http 0 → 100644
  1 +### 请求 /system/mail-template/send-mail 接口 => 成功
  2 +POST {{baseUrl}}/system/mail-template/send-mail
  3 +Authorization: Bearer {{token}}
  4 +Content-Type: application/json
  5 +tenant-id: {{adminTenentId}}
  6 +
  7 +{
  8 + "templateCode": "test_01",
  9 + "mail": "7685413@qq.com",
  10 + "templateParams": {
  11 + "key01": "value01",
  12 + "key02": "value02"
  13 + }
  14 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.*;
  6 +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateSendReqVO;
  7 +import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  9 +import cn.iocoder.yudao.module.system.service.mail.MailSendService;
  10 +import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
  11 +import io.swagger.annotations.Api;
  12 +import io.swagger.annotations.ApiImplicitParam;
  13 +import io.swagger.annotations.ApiOperation;
  14 +import org.springframework.security.access.prepost.PreAuthorize;
  15 +import org.springframework.web.bind.annotation.*;
  16 +
  17 +import javax.annotation.Resource;
  18 +import javax.validation.Valid;
  19 +import java.util.List;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  22 +
  23 +@Api(tags = "管理后台 - 邮件模版")
  24 +@RestController
  25 +@RequestMapping("/system/mail-template")
  26 +public class MailTemplateController {
  27 +
  28 + @Resource
  29 + private MailTemplateService mailTempleService;
  30 + @Resource
  31 + private MailSendService mailSendService;
  32 +
  33 + @PostMapping("/create")
  34 + @ApiOperation("创建邮件模版")
  35 + @PreAuthorize("@ss.hasPermission('system:mail-template:create')")
  36 + public CommonResult<Long> createMailTemplate(@Valid @RequestBody MailTemplateCreateReqVO createReqVO){
  37 + return success(mailTempleService.createMailTemplate(createReqVO));
  38 + }
  39 +
  40 + @PutMapping("/update")
  41 + @ApiOperation("修改邮件模版")
  42 + @PreAuthorize("@ss.hasPermission('system:mail-template:update')")
  43 + public CommonResult<Boolean> updateMailTemplate(@Valid @RequestBody MailTemplateUpdateReqVO updateReqVO){
  44 + mailTempleService.updateMailTemplate(updateReqVO);
  45 + return success(true);
  46 + }
  47 +
  48 + @DeleteMapping("/delete")
  49 + @ApiOperation("删除邮件模版")
  50 + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
  51 + @PreAuthorize("@ss.hasPermission('system:mail-template:delete')")
  52 + public CommonResult<Boolean> deleteMailTemplate(@RequestParam("id") Long id) {
  53 + mailTempleService.deleteMailTemplate(id);
  54 + return success(true);
  55 + }
  56 +
  57 + @GetMapping("/get")
  58 + @ApiOperation("获得邮件模版")
  59 + @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
  60 + @PreAuthorize("@ss.hasPermission('system:mail-template:get')")
  61 + public CommonResult<MailTemplateRespVO> getMailTemplate(@RequestParam("id") Long id) {
  62 + MailTemplateDO mailTemplateDO = mailTempleService.getMailTemplate(id);
  63 + return success(MailTemplateConvert.INSTANCE.convert(mailTemplateDO));
  64 + }
  65 +
  66 + @GetMapping("/page")
  67 + @ApiOperation("获得邮件模版分页")
  68 + @PreAuthorize("@ss.hasPermission('system:mail-template:query')")
  69 + public CommonResult<PageResult<MailTemplateRespVO>> getMailTemplatePage(@Valid MailTemplatePageReqVO pageReqVO) {
  70 + PageResult<MailTemplateDO> pageResult = mailTempleService.getMailTemplatePage(pageReqVO);
  71 + return success(MailTemplateConvert.INSTANCE.convertPage(pageResult));
  72 + }
  73 +
  74 + @GetMapping("/list-all-simple")
  75 + @ApiOperation(value = "获得邮件模版精简列表")
  76 + public CommonResult<List<MailTemplateSimpleRespVO>> getSimpleTemplateList() {
  77 + List<MailTemplateDO> list = mailTempleService.getMailTemplateList();
  78 + return success(MailTemplateConvert.INSTANCE.convertList02(list));
  79 + }
  80 +
  81 + @PostMapping("/send-mail")
  82 + @ApiOperation("发送短信")
  83 + @PreAuthorize("@ss.hasPermission('system:mail-template:send-mail')")
  84 + public CommonResult<Long> sendMail(@Valid @RequestBody MailTemplateSendReqVO sendReqVO) {
  85 + return success(mailSendService.sendSingleMailToAdmin(sendReqVO.getMail(), null,
  86 + sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
  87 + }
  88 +
  89 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountBaseVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account;
  2 +
  3 +import io.swagger.annotations.ApiModelProperty;
  4 +import lombok.Data;
  5 +
  6 +import javax.validation.constraints.Email;
  7 +import javax.validation.constraints.NotNull;
  8 +
  9 +/**
  10 + * 邮箱账号 Base VO,提供给添加、修改、详细的子 VO 使用
  11 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  12 + */
  13 +@Data
  14 +public class MailAccountBaseVO {
  15 +
  16 + @ApiModelProperty(value = "邮箱", required = true, example = "yudaoyuanma@123.com")
  17 + @NotNull(message = "邮箱不能为空")
  18 + @Email(message = "必须是 Email 格式")
  19 + private String mail;
  20 +
  21 + @ApiModelProperty(value = "用户名", required = true, example = "yudao")
  22 + @NotNull(message = "用户名不能为空")
  23 + private String username;
  24 +
  25 + @ApiModelProperty(value = "密码", required = true, example = "123456")
  26 + @NotNull(message = "密码必填")
  27 + private String password;
  28 +
  29 + @ApiModelProperty(value = "SMTP 服务器域名", required = true, example = "www.iocoder.cn")
  30 + @NotNull(message = "SMTP 服务器域名不能为空")
  31 + private String host;
  32 +
  33 + @ApiModelProperty(value = "SMTP 服务器端口", required = true, example = "80")
  34 + @NotNull(message = "SMTP 服务器端口不能为空")
  35 + private Integer port;
  36 +
  37 + @ApiModelProperty(value = "是否开启 ssl", required = true, example = "true")
  38 + @NotNull(message = "是否开启 ssl 必填")
  39 + private Boolean sslEnable;
  40 +
  41 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountCreateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.constraints.NotNull;
  10 +
  11 +@ApiModel("管理后台 - 邮箱账号创建 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class MailAccountCreateReqVO extends MailAccountBaseVO {
  16 +
  17 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.annotations.ApiModel;
  5 +import io.swagger.annotations.ApiModelProperty;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +import lombok.ToString;
  9 +
  10 +@ApiModel("管理后台 - 邮箱账号分页 Request VO")
  11 +@Data
  12 +@EqualsAndHashCode(callSuper = true)
  13 +@ToString(callSuper = true)
  14 +public class MailAccountPageReqVO extends PageParam {
  15 +
  16 + @ApiModelProperty(value = "邮箱", required = true, example = "yudaoyuanma@123.com")
  17 + private String mail;
  18 +
  19 + @ApiModelProperty(value = "用户名" , required = true , example = "yudao")
  20 + private String username;
  21 +
  22 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.constraints.NotNull;
  10 +import java.time.LocalDateTime;
  11 +
  12 +@ApiModel("管理后台 - 邮箱账号 Response VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class MailAccountRespVO extends MailAccountBaseVO {
  17 +
  18 + @ApiModelProperty(value = "编号", required = true, example = "1024")
  19 + @NotNull(message = "编号不能为空")
  20 + private Long id;
  21 +
  22 + @ApiModelProperty(value = "创建时间", required = true)
  23 + private LocalDateTime createTime;
  24 +
  25 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountSimpleRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +@ApiModel("管理后台 - 邮箱账号的精简 Response VO")
  8 +@Data
  9 +public class MailAccountSimpleRespVO {
  10 +
  11 + @ApiModelProperty(value = "邮箱编号", required = true, example = "1024")
  12 + private Long id;
  13 +
  14 + @ApiModelProperty(value = "邮箱", required = true, example = "768541388@qq.com")
  15 + private String mail;
  16 +
  17 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/account/MailAccountUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.account;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.constraints.NotNull;
  10 +
  11 +@ApiModel("管理后台 - 邮箱账号修改 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class MailAccountUpdateReqVO extends MailAccountBaseVO {
  16 +
  17 + @ApiModelProperty(value = "编号", required = true, example = "1024")
  18 + @NotNull(message = "编号不能为空")
  19 + private Long id;
  20 +
  21 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogBaseVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.log;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import java.time.LocalDateTime;
  8 +import io.swagger.annotations.*;
  9 +import javax.validation.constraints.*;
  10 +import org.springframework.format.annotation.DateTimeFormat;
  11 +
  12 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  13 +
  14 +/**
  15 +* 邮件日志 Base VO,提供给添加、修改、详细的子 VO 使用
  16 +* 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  17 +*/
  18 +@Data
  19 +public class MailLogBaseVO {
  20 +
  21 + @ApiModelProperty(value = "用户编号", example = "30883")
  22 + private Long userId;
  23 +
  24 + @ApiModelProperty(value = "用户类型", example = "2", notes = "参见 UserTypeEnum 枚举")
  25 + private Byte userType;
  26 +
  27 + @ApiModelProperty(value = "接收邮箱地址", required = true, example = "76854@qq.com")
  28 + @NotNull(message = "接收邮箱地址不能为空")
  29 + private String toMail;
  30 +
  31 + @ApiModelProperty(value = "邮箱账号编号", required = true, example = "18107")
  32 + @NotNull(message = "邮箱账号编号不能为空")
  33 + private Long accountId;
  34 +
  35 + @ApiModelProperty(value = "发送邮箱地址", required = true, example = "85757@qq.com")
  36 + @NotNull(message = "发送邮箱地址不能为空")
  37 + private String fromMail;
  38 +
  39 + @ApiModelProperty(value = "模板编号", required = true, example = "5678")
  40 + @NotNull(message = "模板编号不能为空")
  41 + private Long templateId;
  42 +
  43 + @ApiModelProperty(value = "模板编码", required = true, example = "test_01")
  44 + @NotNull(message = "模板编码不能为空")
  45 + private String templateCode;
  46 +
  47 + @ApiModelProperty(value = "模版发送人名称", example = "李四")
  48 + private String templateNickname;
  49 +
  50 + @ApiModelProperty(value = "邮件标题", required = true, example = "测试标题")
  51 + @NotNull(message = "邮件标题不能为空")
  52 + private String templateTitle;
  53 +
  54 + @ApiModelProperty(value = "邮件内容", required = true, example = "测试内容")
  55 + @NotNull(message = "邮件内容不能为空")
  56 + private String templateContent;
  57 +
  58 + @ApiModelProperty(value = "邮件参数", required = true)
  59 + @NotNull(message = "邮件参数不能为空")
  60 + private Map<String, Object> templateParams;
  61 +
  62 + @ApiModelProperty(value = "发送状态", required = true, example = "1", notes = "参见 MailSendStatusEnum 枚举")
  63 + @NotNull(message = "发送状态不能为空")
  64 + private Byte sendStatus;
  65 +
  66 + @ApiModelProperty(value = "发送时间")
  67 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  68 + private LocalDateTime sendTime;
  69 +
  70 + @ApiModelProperty(value = "发送返回的消息 ID", example = "28568")
  71 + private String sendMessageId;
  72 +
  73 + @ApiModelProperty(value = "发送异常")
  74 + private String sendException;
  75 +
  76 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogPageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.log;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.annotations.ApiModel;
  5 +import io.swagger.annotations.ApiModelProperty;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +import lombok.ToString;
  9 +import org.springframework.format.annotation.DateTimeFormat;
  10 +
  11 +import java.sql.Timestamp;
  12 +import java.time.LocalDateTime;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  15 +
  16 +@ApiModel("管理后台 - 邮箱日志分页 Request VO")
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +public class MailLogPageReqVO extends PageParam {
  21 +
  22 + @ApiModelProperty(value = "用户编号", example = "30883")
  23 + private Long userId;
  24 +
  25 + @ApiModelProperty(value = "用户类型", example = "2", notes = "参见 UserTypeEnum 枚举")
  26 + private Integer userType;
  27 +
  28 + @ApiModelProperty(value = "接收邮箱地址", example = "76854@qq.com", notes = "模糊匹配")
  29 + private String toMail;
  30 +
  31 + @ApiModelProperty(value = "邮箱账号编号", example = "18107")
  32 + private Long accountId;
  33 +
  34 + @ApiModelProperty(value = "模板编号", example = "5678")
  35 + private Long templateId;
  36 +
  37 + @ApiModelProperty(value = "发送状态", example = "1", notes = "参见 MailSendStatusEnum 枚举")
  38 + private Integer sendStatus;
  39 +
  40 + @ApiModelProperty(value = "发送时间")
  41 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  42 + private LocalDateTime[] sendTime;
  43 +
  44 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/log/MailLogRespVO.java 0 → 100755
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.log;
  2 +
  3 +import lombok.*;
  4 +import java.time.LocalDateTime;
  5 +import io.swagger.annotations.*;
  6 +
  7 +@ApiModel("管理后台 - 邮件日志 Response VO")
  8 +@Data
  9 +@EqualsAndHashCode(callSuper = true)
  10 +@ToString(callSuper = true)
  11 +public class MailLogRespVO extends MailLogBaseVO {
  12 +
  13 + @ApiModelProperty(value = "编号", required = true, example = "31020")
  14 + private Long id;
  15 +
  16 + @ApiModelProperty(value = "创建时间", required = true)
  17 + private LocalDateTime createTime;
  18 +
  19 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateBaseVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template;
  2 +
  3 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.NotEmpty;
  8 +import javax.validation.constraints.NotNull;
  9 +
  10 +/**
  11 + * 邮件模版 Base VO,提供给添加、修改、详细的子 VO 使用
  12 + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成
  13 + */
  14 +@Data
  15 +public class MailTemplateBaseVO {
  16 +
  17 + @ApiModelProperty(value = "模版名称", required = true, example = "测试名字")
  18 + @NotNull(message = "名称不能为空")
  19 + private String name;
  20 +
  21 + @ApiModelProperty(value = "模版编号", required = true, example = "test")
  22 + @NotNull(message = "模版编号不能为空")
  23 + private String code;
  24 +
  25 + @ApiModelProperty(value = "发送的邮箱账号编号", required = true, example = "1")
  26 + @NotNull(message = "发送的邮箱账号编号不能为空")
  27 + private Long accountId;
  28 +
  29 + @ApiModelProperty(value = "发送人名称", example = "芋头")
  30 + private String nickname;
  31 +
  32 + @ApiModelProperty(value = "标题", required = true, example = "注册成功")
  33 + @NotEmpty(message = "标题不能为空")
  34 + private String title;
  35 +
  36 + @ApiModelProperty(value = "内容", required = true, example = "你好,注册成功啦")
  37 + @NotEmpty(message = "内容不能为空")
  38 + private String content;
  39 +
  40 + @ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举")
  41 + @NotNull(message = "状态不能为空")
  42 + private Integer status;
  43 +
  44 + @ApiModelProperty(value = "备注", example = "奥特曼")
  45 + private String remark;
  46 +
  47 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateCreateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +import lombok.ToString;
  7 +
  8 +@ApiModel("管理后台 - 邮件模版创建 Request VO")
  9 +@Data
  10 +@EqualsAndHashCode(callSuper = true)
  11 +@ToString(callSuper = true)
  12 +public class MailTemplateCreateReqVO extends MailTemplateBaseVO {
  13 +
  14 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplatePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import io.swagger.annotations.ApiModel;
  5 +import io.swagger.annotations.ApiModelProperty;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +import lombok.ToString;
  9 +import org.springframework.format.annotation.DateTimeFormat;
  10 +
  11 +import java.time.LocalDateTime;
  12 +
  13 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  14 +
  15 +@ApiModel("管理后台 - 邮件模版分页 Request VO")
  16 +@Data
  17 +@EqualsAndHashCode(callSuper = true)
  18 +@ToString(callSuper = true)
  19 +public class MailTemplatePageReqVO extends PageParam {
  20 +
  21 + @ApiModelProperty(value = "状态", example = "1", notes = "参见 CommonStatusEnum 枚举")
  22 + private Integer status;
  23 +
  24 + @ApiModelProperty(value = "标识", example = "code_1024", notes = "模糊匹配")
  25 + private String code;
  26 +
  27 + @ApiModelProperty(value = "名称", example = "芋头", notes = "模糊匹配")
  28 + private String name;
  29 +
  30 + @ApiModelProperty(value = "账号编号", example = "2048")
  31 + private Long accountId;
  32 +
  33 + @ApiModelProperty(value = "创建时间")
  34 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  35 + private LocalDateTime[] createTime;
  36 +
  37 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import java.time.LocalDateTime;
  10 +import java.util.List;
  11 +
  12 +@ApiModel("管理后台 - 邮件末班 Response VO")
  13 +@Data
  14 +@EqualsAndHashCode(callSuper = true)
  15 +@ToString(callSuper = true)
  16 +public class MailTemplateRespVO extends MailTemplateBaseVO {
  17 +
  18 + @ApiModelProperty(value = "编号", required = true, example = "1024")
  19 + private Long id;
  20 +
  21 + @ApiModelProperty(value = "参数数组", example = "name,code")
  22 + private List<String> params;
  23 +
  24 + @ApiModelProperty(value = "创建时间", required = true)
  25 + private LocalDateTime createTime;
  26 +
  27 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSendReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.NotEmpty;
  8 +import javax.validation.constraints.NotNull;
  9 +import java.util.Map;
  10 +
  11 +@ApiModel("管理后台 - 邮件发送 Req VO")
  12 +@Data
  13 +public class MailTemplateSendReqVO {
  14 +
  15 + @ApiModelProperty(value = "接收邮箱", required = true, example = "7685413@qq.com")
  16 + @NotEmpty(message = "接收邮箱不能为空")
  17 + private String mail;
  18 +
  19 + @ApiModelProperty(value = "模板编码", required = true, example = "test_01")
  20 + @NotNull(message = "模板编码不能为空")
  21 + private String templateCode;
  22 +
  23 + @ApiModelProperty(value = "模板参数")
  24 + private Map<String, Object> templateParams;
  25 +
  26 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSimpleRespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +
  7 +@ApiModel("管理后台 - 邮件模版的精简 Response VO")
  8 +@Data
  9 +public class MailTemplateSimpleRespVO {
  10 +
  11 + @ApiModelProperty(value = "模版编号", required = true, example = "1024")
  12 + private Long id;
  13 +
  14 + @ApiModelProperty(value = "模版名字", required = true, example = "哒哒哒")
  15 + private String name;
  16 +
  17 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateUpdateReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.controller.admin.mail.vo.template;
  2 +
  3 +import io.swagger.annotations.ApiModel;
  4 +import io.swagger.annotations.ApiModelProperty;
  5 +import lombok.Data;
  6 +import lombok.EqualsAndHashCode;
  7 +import lombok.ToString;
  8 +
  9 +import javax.validation.constraints.NotNull;
  10 +
  11 +@ApiModel("管理后台 - 邮件模版修改 Request VO")
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +@ToString(callSuper = true)
  15 +public class MailTemplateUpdateReqVO extends MailTemplateBaseVO {
  16 +
  17 + @ApiModelProperty(value = "编号", required = true, example = "1024")
  18 + @NotNull(message = "编号不能为空")
  19 + private Long id;
  20 +
  21 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/mail/MailAccountConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.convert.mail;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.hutool.extra.mail.MailAccount;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.*;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  8 +import org.mapstruct.Mapper;
  9 +import org.mapstruct.factory.Mappers;
  10 +
  11 +import java.util.List;
  12 +
  13 +@Mapper
  14 +public interface MailAccountConvert {
  15 +
  16 + MailAccountConvert INSTANCE = Mappers.getMapper(MailAccountConvert.class);
  17 +
  18 + MailAccountDO convert(MailAccountCreateReqVO bean);
  19 +
  20 + MailAccountDO convert(MailAccountUpdateReqVO bean);
  21 +
  22 + MailAccountRespVO convert(MailAccountDO bean);
  23 +
  24 + PageResult<MailAccountBaseVO> convertPage(PageResult<MailAccountDO> pageResult);
  25 +
  26 + List<MailAccountSimpleRespVO> convertList02(List<MailAccountDO> list);
  27 +
  28 + default MailAccount convert(MailAccountDO account, String nickname) {
  29 + String from = StrUtil.isNotEmpty(nickname) ? nickname + " <" + account.getMail() + ">" : account.getMail();
  30 + return new MailAccount().setFrom(from).setAuth(true)
  31 + .setUser(account.getUsername()).setPass(account.getPassword())
  32 + .setHost(account.getHost()).setPort(account.getPort()).setSslEnable(account.getSslEnable());
  33 + }
  34 +
  35 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/mail/MailLogConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.convert.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogRespVO;
  5 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
  6 +import org.mapstruct.Mapper;
  7 +import org.mapstruct.factory.Mappers;
  8 +
  9 +@Mapper
  10 +public interface MailLogConvert {
  11 +
  12 + MailLogConvert INSTANCE = Mappers.getMapper(MailLogConvert.class);
  13 +
  14 + PageResult<MailLogRespVO> convertPage(PageResult<MailLogDO> pageResult);
  15 +
  16 + MailLogRespVO convert(MailLogDO bean);
  17 +
  18 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/mail/MailTemplateConvert.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.convert.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.*;
  5 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  6 +import org.mapstruct.Mapper;
  7 +import org.mapstruct.factory.Mappers;
  8 +
  9 +import java.util.List;
  10 +
  11 +@Mapper
  12 +public interface MailTemplateConvert {
  13 +
  14 + MailTemplateConvert INSTANCE = Mappers.getMapper(MailTemplateConvert.class);
  15 +
  16 + MailTemplateDO convert(MailTemplateUpdateReqVO bean);
  17 +
  18 + MailTemplateDO convert(MailTemplateCreateReqVO bean);
  19 +
  20 + MailTemplateRespVO convert(MailTemplateDO bean);
  21 +
  22 + PageResult<MailTemplateRespVO> convertPage(PageResult<MailTemplateDO> pageResult);
  23 +
  24 + List<MailTemplateSimpleRespVO> convertList02(List<MailTemplateDO> list);
  25 +
  26 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailAccountDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.dataobject.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import com.baomidou.mybatisplus.annotation.TableId;
  5 +import com.baomidou.mybatisplus.annotation.TableName;
  6 +import lombok.Data;
  7 +import lombok.EqualsAndHashCode;
  8 +
  9 +/**
  10 + * 邮箱账号 DO
  11 + *
  12 + * 用途:配置发送邮箱的账号
  13 + *
  14 + * @author wangjingyi
  15 + * @since 2022-03-21
  16 + */
  17 +@TableName(value = "system_mail_account", autoResultMap = true)
  18 +@Data
  19 +@EqualsAndHashCode(callSuper = true)
  20 +public class MailAccountDO extends BaseDO {
  21 +
  22 + /**
  23 + * 主键
  24 + */
  25 + @TableId
  26 + private Long id;
  27 + /**
  28 + * 邮箱
  29 + */
  30 + private String mail;
  31 +
  32 + /**
  33 + * 用户名
  34 + */
  35 + private String username;
  36 + /**
  37 + * 密码
  38 + */
  39 + private String password;
  40 + /**
  41 + * SMTP 服务器域名
  42 + */
  43 + private String host;
  44 + /**
  45 + * SMTP 服务器端口
  46 + */
  47 + private Integer port;
  48 + /**
  49 + * 是否开启 SSL
  50 + */
  51 + private Boolean sslEnable;
  52 +
  53 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailLogDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.dataobject.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
  6 +import com.baomidou.mybatisplus.annotation.TableField;
  7 +import com.baomidou.mybatisplus.annotation.TableName;
  8 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
  9 +import lombok.*;
  10 +
  11 +import java.io.Serializable;
  12 +import java.util.Date;
  13 +import java.util.Map;
  14 +
  15 +/**
  16 + * 邮箱日志 DO
  17 + * 记录每一次邮件的发送
  18 + *
  19 + * @author wangjingyi
  20 + * @since 2022-03-21
  21 + */
  22 +@TableName(value = "system_mail_log", autoResultMap = true)
  23 +@Data
  24 +@EqualsAndHashCode(callSuper = true)
  25 +@ToString(callSuper = true)
  26 +@Builder
  27 +@AllArgsConstructor
  28 +@NoArgsConstructor
  29 +public class MailLogDO extends BaseDO implements Serializable {
  30 +
  31 + /**
  32 + * 日志编号,自增
  33 + */
  34 + private Long id;
  35 +
  36 + /**
  37 + * 用户编码
  38 + */
  39 + private Long userId;
  40 + /**
  41 + * 用户类型
  42 + *
  43 + * 枚举 {@link UserTypeEnum}
  44 + */
  45 + private Integer userType;
  46 + /**
  47 + * 接收邮箱地址
  48 + */
  49 + private String toMail;
  50 +
  51 + /**
  52 + * 邮箱账号编号
  53 + *
  54 + * 关联 {@link MailAccountDO#getId()}
  55 + */
  56 + private Long accountId;
  57 + /**
  58 + * 发送邮箱地址
  59 + *
  60 + * 冗余 {@link MailAccountDO#getMail()}
  61 + */
  62 + private String fromMail;
  63 +
  64 + // ========= 模板相关字段 =========
  65 + /**
  66 + * 模版编号
  67 + *
  68 + * 关联 {@link MailTemplateDO#getId()}
  69 + */
  70 + private Long templateId;
  71 + /**
  72 + * 模版编码
  73 + *
  74 + * 冗余 {@link MailTemplateDO#getCode()}
  75 + */
  76 + private String templateCode;
  77 + /**
  78 + * 模版发送人名称
  79 + *
  80 + * 冗余 {@link MailTemplateDO#getNickname()}
  81 + */
  82 + private String templateNickname;
  83 + /**
  84 + * 模版标题
  85 + */
  86 + private String templateTitle;
  87 + /**
  88 + * 模版内容
  89 + *
  90 + * 基于 {@link MailTemplateDO#getContent()} 格式化后的内容
  91 + */
  92 + private String templateContent;
  93 + /**
  94 + * 模版参数
  95 + *
  96 + * 基于 {@link MailTemplateDO#getParams()} 输入后的参数
  97 + */
  98 + @TableField(typeHandler = JacksonTypeHandler.class)
  99 + private Map<String, Object> templateParams;
  100 +
  101 + // ========= 发送相关字段 =========
  102 + /**
  103 + * 发送状态
  104 + *
  105 + * 枚举 {@link MailSendStatusEnum}
  106 + */
  107 + private Integer sendStatus;
  108 + /**
  109 + * 发送时间
  110 + */
  111 + private Date sendTime;
  112 + /**
  113 + * 发送返回的消息 ID
  114 + */
  115 + private String sendMessageId;
  116 + /**
  117 + * 发送异常
  118 + */
  119 + private String sendException;
  120 +
  121 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/mail/MailTemplateDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.dataobject.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  5 +import com.baomidou.mybatisplus.annotation.TableField;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
  8 +import lombok.Data;
  9 +import lombok.EqualsAndHashCode;
  10 +
  11 +import java.util.List;
  12 +
  13 +/**
  14 + * 邮件模版 DO
  15 + *
  16 + * @author wangjingyi
  17 + * @since 2022-03-21
  18 + */
  19 +@TableName(value = "system_mail_template", autoResultMap = true)
  20 +@Data
  21 +@EqualsAndHashCode(callSuper = true)
  22 +public class MailTemplateDO extends BaseDO {
  23 +
  24 + /**
  25 + * 主键
  26 + */
  27 + private Long id;
  28 + /**
  29 + * 模版名称
  30 + */
  31 + private String name;
  32 + /**
  33 + * 模版编号
  34 + */
  35 + private String code;
  36 + /**
  37 + * 发送的邮箱账号编号
  38 + *
  39 + * 关联 {@link MailAccountDO#getId()}
  40 + */
  41 + private Long accountId;
  42 +
  43 + /**
  44 + * 发送人名称
  45 + */
  46 + private String nickname;
  47 + /**
  48 + * 标题
  49 + */
  50 + private String title;
  51 + /**
  52 + * 内容
  53 + */
  54 + private String content;
  55 + /**
  56 + * 参数数组(自动根据内容生成)
  57 + */
  58 + @TableField(typeHandler = JacksonTypeHandler.class)
  59 + private List<String> params;
  60 + /**
  61 + * 状态
  62 + *
  63 + * 枚举 {@link CommonStatusEnum}
  64 + */
  65 + private Integer status;
  66 + /**
  67 + * 备注
  68 + */
  69 + private String remark;
  70 +
  71 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java
... ... @@ -6,9 +6,7 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqV
6 6 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
7 7 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
8 8 import org.apache.ibatis.annotations.Mapper;
9   -import org.apache.ibatis.annotations.Select;
10 9  
11   -import java.time.LocalDateTime;
12 10 import java.util.List;
13 11  
14 12 @Mapper
... ... @@ -30,7 +28,4 @@ public interface DeptMapper extends BaseMapperX&lt;DeptDO&gt; {
30 28 return selectCount(DeptDO::getParentId, parentId);
31 29 }
32 30  
33   - @Select("SELECT COUNT(*) FROM system_dept WHERE update_time > #{maxUpdateTime}")
34   - Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
35   -
36 31 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailAccountMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.mysql.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
  7 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +
  11 +@Mapper
  12 +public interface MailAccountMapper extends BaseMapperX<MailAccountDO> {
  13 +
  14 + default PageResult<MailAccountDO> selectPage(MailAccountPageReqVO pageReqVO) {
  15 + return selectPage(pageReqVO, new LambdaQueryWrapperX<MailAccountDO>()
  16 + .likeIfPresent(MailAccountDO::getMail, pageReqVO.getMail())
  17 + .likeIfPresent(MailAccountDO::getUsername , pageReqVO.getUsername()));
  18 + }
  19 +
  20 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailLogMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.mysql.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +@Mapper
  11 +public interface MailLogMapper extends BaseMapperX<MailLogDO> {
  12 +
  13 + default PageResult<MailLogDO> selectPage(MailLogPageReqVO reqVO) {
  14 + return selectPage(reqVO, new LambdaQueryWrapperX<MailLogDO>()
  15 + .eqIfPresent(MailLogDO::getUserId, reqVO.getUserId())
  16 + .eqIfPresent(MailLogDO::getUserType, reqVO.getUserType())
  17 + .likeIfPresent(MailLogDO::getToMail, reqVO.getToMail())
  18 + .eqIfPresent(MailLogDO::getAccountId, reqVO.getAccountId())
  19 + .eqIfPresent(MailLogDO::getTemplateId, reqVO.getTemplateId())
  20 + .eqIfPresent(MailLogDO::getSendStatus, reqVO.getSendStatus())
  21 + .betweenIfPresent(MailLogDO::getSendTime, reqVO.getSendTime())
  22 + .orderByDesc(MailLogDO::getId));
  23 + }
  24 +
  25 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.dal.mysql.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  5 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
  7 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  9 +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
  10 +import org.apache.ibatis.annotations.Mapper;
  11 +import org.apache.ibatis.annotations.Select;
  12 +
  13 +import java.util.Date;
  14 +
  15 +@Mapper
  16 +public interface MailTemplateMapper extends BaseMapperX<MailTemplateDO> {
  17 +
  18 + default PageResult<MailTemplateDO> selectPage(MailTemplatePageReqVO pageReqVO){
  19 + return selectPage(pageReqVO , new LambdaQueryWrapperX<MailTemplateDO>()
  20 + .eqIfPresent(MailTemplateDO::getStatus, pageReqVO.getStatus())
  21 + .likeIfPresent(MailTemplateDO::getCode, pageReqVO.getCode())
  22 + .likeIfPresent(MailTemplateDO::getName, pageReqVO.getName())
  23 + .eqIfPresent(MailTemplateDO::getAccountId, pageReqVO.getAccountId())
  24 + .betweenIfPresent(MailTemplateDO::getCreateTime, pageReqVO.getCreateTime()));
  25 + }
  26 +
  27 + default Long selectCountByAccountId(Long accountId) {
  28 + return selectCount(MailTemplateDO::getAccountId, accountId);
  29 + }
  30 +
  31 + default MailTemplateDO selectByCode(String code) {
  32 + return selectOne(MailTemplateDO::getCode, code);
  33 + }
  34 +
  35 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/oauth2/OAuth2ClientMapper.java
... ... @@ -6,9 +6,6 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 6 import cn.iocoder.yudao.module.system.controller.admin.oauth2.vo.client.OAuth2ClientPageReqVO;
7 7 import cn.iocoder.yudao.module.system.dal.dataobject.oauth2.OAuth2ClientDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9   -import org.apache.ibatis.annotations.Select;
10   -
11   -import java.time.LocalDateTime;
12 9  
13 10  
14 11 /**
... ... @@ -30,7 +27,4 @@ public interface OAuth2ClientMapper extends BaseMapperX&lt;OAuth2ClientDO&gt; {
30 27 return selectOne(OAuth2ClientDO::getClientId, clientId);
31 28 }
32 29  
33   - @Select("SELECT COUNT(*) FROM system_oauth2_client WHERE update_time > #{maxUpdateTime}")
34   - int selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
35   -
36 30 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/MenuMapper.java
... ... @@ -6,9 +6,7 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuLi
6 6 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
7 7 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
8 8 import org.apache.ibatis.annotations.Mapper;
9   -import org.apache.ibatis.annotations.Select;
10 9  
11   -import java.time.LocalDateTime;
12 10 import java.util.List;
13 11  
14 12 @Mapper
... ... @@ -28,7 +26,4 @@ public interface MenuMapper extends BaseMapperX&lt;MenuDO&gt; {
28 26 .eqIfPresent(MenuDO::getStatus, reqVO.getStatus()));
29 27 }
30 28  
31   - @Select("SELECT COUNT(*) FROM system_menu WHERE update_time > #{maxUpdateTime}")
32   - Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
33   -
34 29 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMapper.java
... ... @@ -8,10 +8,8 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleEx
8 8 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RolePageReqVO;
9 9 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
10 10 import org.apache.ibatis.annotations.Mapper;
11   -import org.apache.ibatis.annotations.Select;
12 11 import org.springframework.lang.Nullable;
13 12  
14   -import java.time.LocalDateTime;
15 13 import java.util.Collection;
16 14 import java.util.List;
17 15  
... ... @@ -47,7 +45,4 @@ public interface RoleMapper extends BaseMapperX&lt;RoleDO&gt; {
47 45 return selectList(RoleDO::getStatus, statuses);
48 46 }
49 47  
50   - @Select("SELECT COUNT(*) FROM system_role WHERE update_time > #{maxUpdateTime}")
51   - Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
52   -
53 48 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuBatchInsertMapper.java deleted
1   -package cn.iocoder.yudao.module.system.dal.mysql.permission;
2   -
3   -import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
4   -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
5   -import org.springframework.stereotype.Repository;
6   -
7   -/**
8   - * 实体 {@link RoleMenuDO} 的批量插入 Mapper
9   - *
10   - * @author 芋道源码
11   - */
12   -@Repository
13   -public class RoleMenuBatchInsertMapper extends ServiceImpl<RoleMenuMapper, RoleMenuDO> {
14   -}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/RoleMenuMapper.java
... ... @@ -2,13 +2,11 @@ package cn.iocoder.yudao.module.system.dal.mysql.permission;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 4 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
5   -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  5 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
6 6 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
7 7 import org.apache.ibatis.annotations.Mapper;
8   -import org.apache.ibatis.annotations.Select;
9 8 import org.springframework.stereotype.Repository;
10 9  
11   -import java.time.LocalDateTime;
12 10 import java.util.Collection;
13 11 import java.util.List;
14 12  
... ... @@ -20,23 +18,21 @@ public interface RoleMenuMapper extends BaseMapperX&lt;RoleMenuDO&gt; {
20 18 }
21 19  
22 20 default List<RoleMenuDO> selectListByRoleId(Long roleId) {
23   - return selectList(new QueryWrapper<RoleMenuDO>().eq("role_id", roleId));
  21 + return selectList(RoleMenuDO::getRoleId, roleId);
24 22 }
25 23  
26 24 default void deleteListByRoleIdAndMenuIds(Long roleId, Collection<Long> menuIds) {
27   - delete(new QueryWrapper<RoleMenuDO>().eq("role_id", roleId)
28   - .in("menu_id", menuIds));
  25 + delete(new LambdaQueryWrapper<RoleMenuDO>()
  26 + .eq(RoleMenuDO::getRoleId, roleId)
  27 + .in(RoleMenuDO::getMenuId, menuIds));
29 28 }
30 29  
31 30 default void deleteListByMenuId(Long menuId) {
32   - delete(new QueryWrapper<RoleMenuDO>().eq("menu_id", menuId));
  31 + delete(new LambdaQueryWrapper<RoleMenuDO>().eq(RoleMenuDO::getMenuId, menuId));
33 32 }
34 33  
35 34 default void deleteListByRoleId(Long roleId) {
36   - delete(new QueryWrapper<RoleMenuDO>().eq("role_id", roleId));
  35 + delete(new LambdaQueryWrapper<RoleMenuDO>().eq(RoleMenuDO::getRoleId, roleId));
37 36 }
38 37  
39   - @Select("SELECT COUNT(*) FROM system_role_menu WHERE update_time > #{maxUpdateTime}")
40   - Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
41   -
42 38 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/UserRoleBatchInsertMapper.java deleted
1   -package cn.iocoder.yudao.module.system.dal.mysql.permission;
2   -
3   -import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
4   -import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
5   -import org.springframework.stereotype.Repository;
6   -
7   -/**
8   - * 实体 {@link UserRoleDO} 的批量插入 Mapper
9   - *
10   - * @author 芋道源码
11   - */
12   -@Repository
13   -public class UserRoleBatchInsertMapper extends ServiceImpl<UserRoleMapper, UserRoleDO> {
14   -}
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/permission/UserRoleMapper.java
... ... @@ -2,11 +2,9 @@ package cn.iocoder.yudao.module.system.dal.mysql.permission;
2 2  
3 3 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 4 import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
5   -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  5 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
6 6 import org.apache.ibatis.annotations.Mapper;
7   -import org.apache.ibatis.annotations.Select;
8 7  
9   -import java.time.LocalDateTime;
10 8 import java.util.Collection;
11 9 import java.util.List;
12 10  
... ... @@ -14,32 +12,25 @@ import java.util.List;
14 12 public interface UserRoleMapper extends BaseMapperX<UserRoleDO> {
15 13  
16 14 default List<UserRoleDO> selectListByUserId(Long userId) {
17   - return selectList(new QueryWrapper<UserRoleDO>().eq("user_id", userId));
18   - }
19   -
20   - default List<UserRoleDO> selectListByRoleId(Long roleId) {
21   - return selectList(new QueryWrapper<UserRoleDO>().eq("role_id", roleId));
  15 + return selectList(UserRoleDO::getUserId, userId);
22 16 }
23 17  
24 18 default void deleteListByUserIdAndRoleIdIds(Long userId, Collection<Long> roleIds) {
25   - delete(new QueryWrapper<UserRoleDO>().eq("user_id", userId)
26   - .in("role_id", roleIds));
  19 + delete(new LambdaQueryWrapper<UserRoleDO>()
  20 + .eq(UserRoleDO::getUserId, userId)
  21 + .in(UserRoleDO::getRoleId, roleIds));
27 22 }
28 23  
29 24 default void deleteListByUserId(Long userId) {
30   - delete(new QueryWrapper<UserRoleDO>().eq("user_id", userId));
  25 + delete(new LambdaQueryWrapper<UserRoleDO>().eq(UserRoleDO::getUserId, userId));
31 26 }
32 27  
33 28 default void deleteListByRoleId(Long roleId) {
34   - delete(new QueryWrapper<UserRoleDO>().eq("role_id", roleId));
  29 + delete(new LambdaQueryWrapper<UserRoleDO>().eq(UserRoleDO::getRoleId, roleId));
35 30 }
36 31  
37   -
38 32 default List<UserRoleDO> selectListByRoleIds(Collection<Long> roleIds) {
39 33 return selectList(UserRoleDO::getRoleId, roleIds);
40 34 }
41 35  
42   - @Select("SELECT COUNT(*) FROM system_user_role WHERE update_time > #{maxUpdateTime}")
43   - Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
44   -
45 36 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sensitiveword/SensitiveWordMapper.java
... ... @@ -7,9 +7,7 @@ import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.Sensitiv
7 7 import cn.iocoder.yudao.module.system.controller.admin.sensitiveword.vo.SensitiveWordPageReqVO;
8 8 import cn.iocoder.yudao.module.system.dal.dataobject.sensitiveword.SensitiveWordDO;
9 9 import org.apache.ibatis.annotations.Mapper;
10   -import org.apache.ibatis.annotations.Select;
11 10  
12   -import java.time.LocalDateTime;
13 11 import java.util.List;
14 12  
15 13 /**
... ... @@ -42,7 +40,4 @@ public interface SensitiveWordMapper extends BaseMapperX&lt;SensitiveWordDO&gt; {
42 40 return selectOne(SensitiveWordDO::getName, name);
43 41 }
44 42  
45   - @Select("SELECT COUNT(*) FROM system_sensitive_word WHERE update_time > #{maxUpdateTime}")
46   - Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
47   -
48 43 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsChannelMapper.java
... ... @@ -6,9 +6,6 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 6 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
7 7 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9   -import org.apache.ibatis.annotations.Select;
10   -
11   -import java.time.LocalDateTime;
12 9  
13 10 @Mapper
14 11 public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
... ... @@ -21,7 +18,4 @@ public interface SmsChannelMapper extends BaseMapperX&lt;SmsChannelDO&gt; {
21 18 .orderByDesc(SmsChannelDO::getId));
22 19 }
23 20  
24   - @Select("SELECT COUNT(*) FROM system_sms_channel WHERE update_time > #{maxUpdateTime}")
25   - Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
26   -
27 21 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsTemplateMapper.java
... ... @@ -7,17 +7,12 @@ import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTempla
7 7 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
8 8 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
9 9 import org.apache.ibatis.annotations.Mapper;
10   -import org.apache.ibatis.annotations.Select;
11 10  
12   -import java.time.LocalDateTime;
13 11 import java.util.List;
14 12  
15 13 @Mapper
16 14 public interface SmsTemplateMapper extends BaseMapperX<SmsTemplateDO> {
17 15  
18   - @Select("SELECT COUNT(*) FROM system_sms_template WHERE update_time > #{maxUpdateTime}")
19   - Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
20   -
21 16 default SmsTemplateDO selectByCode(String code) {
22 17 return selectOne(SmsTemplateDO::getCode, code);
23 18 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java
1 1 package cn.iocoder.yudao.module.system.dal.mysql.tenant;
2 2  
3   -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExportReqVO;
4   -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
5   -import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
6 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 4 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
8 5 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  6 +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExportReqVO;
  7 +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
9 9 import org.apache.ibatis.annotations.Mapper;
10   -import org.apache.ibatis.annotations.Select;
11 10  
12   -import java.time.LocalDateTime;
13 11 import java.util.List;
14 12  
15 13 /**
... ... @@ -52,7 +50,4 @@ public interface TenantMapper extends BaseMapperX&lt;TenantDO&gt; {
52 50 return selectList(TenantDO::getPackageId, packageId);
53 51 }
54 52  
55   - @Select("SELECT COUNT(*) FROM system_tenant WHERE update_time > #{maxUpdateTime}")
56   - Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
57   -
58 53 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailAccountRefreshConsumer.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.consumer.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
  4 +import cn.iocoder.yudao.module.system.mq.message.mail.MailAccountRefreshMessage;
  5 +import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage;
  6 +import cn.iocoder.yudao.module.system.service.mail.MailAccountService;
  7 +import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.stereotype.Component;
  10 +
  11 +import javax.annotation.Resource;
  12 +
  13 +/**
  14 + * 针对 {@link MailAccountRefreshMessage} 的消费者
  15 + *
  16 + * @author wangjingyi
  17 + */
  18 +@Component
  19 +@Slf4j
  20 +public class MailAccountRefreshConsumer extends AbstractChannelMessageListener<MailAccountRefreshMessage> {
  21 +
  22 + @Resource
  23 + private MailAccountService mailAccountService;
  24 +
  25 + @Override
  26 + public void onMessage(MailAccountRefreshMessage message) {
  27 + log.info("[onMessage][收到 Mail Account 刷新信息]");
  28 + mailAccountService.initLocalCache();
  29 + }
  30 +
  31 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailSendConsumer.java
... ... @@ -2,17 +2,29 @@ package cn.iocoder.yudao.module.system.mq.consumer.mail;
2 2  
3 3 import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessageListener;
4 4 import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
  5 +import cn.iocoder.yudao.module.system.mq.message.sms.SmsSendMessage;
  6 +import cn.iocoder.yudao.module.system.service.mail.MailSendService;
5 7 import lombok.extern.slf4j.Slf4j;
6 8 import org.springframework.stereotype.Component;
7 9  
8   -// TODO 芋艿:这个暂未实现
  10 +import javax.annotation.Resource;
  11 +
  12 +/**
  13 + * 针对 {@link MailSendMessage} 的消费者
  14 + *
  15 + * @author 芋道源码
  16 + */
9 17 @Component
10 18 @Slf4j
11 19 public class MailSendConsumer extends AbstractStreamMessageListener<MailSendMessage> {
12 20  
  21 + @Resource
  22 + private MailSendService mailSendService;
  23 +
13 24 @Override
14 25 public void onMessage(MailSendMessage message) {
15 26 log.info("[onMessage][消息内容({})]", message);
  27 + mailSendService.doSendMail(message);
16 28 }
17 29  
18 30 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/mail/MailTemplateRefreshConsumer.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.consumer.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
  4 +import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage;
  5 +import cn.iocoder.yudao.module.system.service.mail.MailTemplateService;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import javax.annotation.Resource;
  10 +
  11 +/**
  12 + * 针对 {@link MailTemplateRefreshMessage} 的消费者
  13 + *
  14 + * @author wangjingyi
  15 + */
  16 +@Component
  17 +@Slf4j
  18 +public class MailTemplateRefreshConsumer extends AbstractChannelMessageListener<MailTemplateRefreshMessage> {
  19 +
  20 + @Resource
  21 + private MailTemplateService mailTemplateService;
  22 +
  23 + @Override
  24 + public void onMessage(MailTemplateRefreshMessage message) {
  25 + log.info("[onMessage][收到 Mail Template 刷新信息]");
  26 + mailTemplateService.initLocalCache();
  27 + }
  28 +
  29 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailAccountRefreshMessage.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.message.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +
  7 +/**
  8 + * 邮箱账号的数据刷新 Message
  9 + *
  10 + * @author wangjingyi
  11 + */
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +public class MailAccountRefreshMessage extends AbstractChannelMessage {
  15 +
  16 + @Override
  17 + public String getChannel() {
  18 + return "system.mail-account.refresh";
  19 + }
  20 +
  21 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailSendMessage.java
... ... @@ -4,8 +4,8 @@ import cn.iocoder.yudao.framework.mq.core.stream.AbstractStreamMessage;
4 4 import lombok.Data;
5 5 import lombok.EqualsAndHashCode;
6 6  
  7 +import javax.validation.constraints.NotEmpty;
7 8 import javax.validation.constraints.NotNull;
8   -import java.util.Map;
9 9  
10 10 /**
11 11 * 邮箱发送消息
... ... @@ -17,28 +17,35 @@ import java.util.Map;
17 17 public class MailSendMessage extends AbstractStreamMessage {
18 18  
19 19 /**
20   - * 邮箱地址
  20 + * 邮件日志编号
21 21 */
22   - @NotNull(message = "邮箱地址不能为空")
23   - private String address;
  22 + @NotNull(message = "邮件日志编号不能为空")
  23 + private Long logId;
24 24 /**
25   - * 短信模板编号
  25 + * 接收邮件地址
26 26 */
27   - @NotNull(message = "短信模板编号不能为空")
28   - private String templateCode;
  27 + @NotNull(message = "接收邮件地址不能为空")
  28 + private String mail;
29 29 /**
30   - * 短信模板参数
  30 + * 邮件账号编号
31 31 */
32   - private Map<String, Object> templateParams;
  32 + @NotNull(message = "邮件账号编号不能为空")
  33 + private Long accountId;
33 34  
34 35 /**
35   - * 用户编号,允许空
  36 + * 邮件发件人
36 37 */
37   - private Integer userId;
  38 + private String nickname;
38 39 /**
39   - * 用户类型,允许空
  40 + * 邮件标题
40 41 */
41   - private Integer userType;
  42 + @NotEmpty(message = "邮件标题不能为空")
  43 + private String title;
  44 + /**
  45 + * 邮件内容
  46 + */
  47 + @NotEmpty(message = "邮件内容不能为空")
  48 + private String content;
42 49  
43 50 @Override
44 51 public String getStreamKey() {
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/mail/MailTemplateRefreshMessage.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.message.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +
  7 +/**
  8 + * 邮箱模板的数据刷新 Message
  9 + *
  10 + * @author wangjingyi
  11 + */
  12 +@Data
  13 +@EqualsAndHashCode(callSuper = true)
  14 +public class MailTemplateRefreshMessage extends AbstractChannelMessage {
  15 +
  16 + @Override
  17 + public String getChannel() {
  18 + return "system.mail-template.refresh";
  19 + }
  20 +
  21 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/mail/MailProducer.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.mq.producer.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
  4 +import cn.iocoder.yudao.module.system.mq.message.mail.MailAccountRefreshMessage;
  5 +import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
  6 +import cn.iocoder.yudao.module.system.mq.message.mail.MailTemplateRefreshMessage;
  7 +import lombok.extern.slf4j.Slf4j;
  8 +import org.springframework.stereotype.Component;
  9 +
  10 +import javax.annotation.Resource;
  11 +
  12 +/**
  13 + * Mail 邮件相关消息的 Producer
  14 + *
  15 + * @author wangjingyi
  16 + * @since 2021/4/19 13:33
  17 + */
  18 +@Slf4j
  19 +@Component
  20 +public class MailProducer {
  21 +
  22 + @Resource
  23 + private RedisMQTemplate redisMQTemplate;
  24 +
  25 + /**
  26 + * 发送 {@link MailTemplateRefreshMessage} 消息
  27 + */
  28 + public void sendMailTemplateRefreshMessage() {
  29 + MailTemplateRefreshMessage message = new MailTemplateRefreshMessage();
  30 + redisMQTemplate.send(message);
  31 + }
  32 +
  33 + /**
  34 + * 发送 {@link MailAccountRefreshMessage} 消息
  35 + */
  36 + public void sendMailAccountRefreshMessage() {
  37 + MailAccountRefreshMessage message = new MailAccountRefreshMessage();
  38 + redisMQTemplate.send(message);
  39 + }
  40 +
  41 + /**
  42 + * 发送 {@link MailSendMessage} 消息
  43 + *
  44 + * @param sendLogId 发送日志编码
  45 + * @param mail 接收邮件地址
  46 + * @param accountId 邮件账号编号
  47 + * @param nickname 邮件发件人
  48 + * @param title 邮件标题
  49 + * @param content 邮件内容
  50 + */
  51 + public void sendMailSendMessage(Long sendLogId, String mail, Long accountId,
  52 + String nickname, String title, String content) {
  53 + MailSendMessage message = new MailSendMessage()
  54 + .setLogId(sendLogId).setMail(mail).setAccountId(accountId)
  55 + .setNickname(nickname).setTitle(title).setContent(content);
  56 + redisMQTemplate.send(message);
  57 + }
  58 +
  59 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java
... ... @@ -17,6 +17,7 @@ import cn.iocoder.yudao.module.system.mq.producer.dept.DeptProducer;
17 17 import com.google.common.collect.ImmutableMap;
18 18 import com.google.common.collect.ImmutableMultimap;
19 19 import com.google.common.collect.Multimap;
  20 +import lombok.Getter;
20 21 import lombok.extern.slf4j.Slf4j;
21 22 import org.springframework.scheduling.annotation.Scheduled;
22 23 import org.springframework.stereotype.Service;
... ... @@ -41,18 +42,12 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
41 42 public class DeptServiceImpl implements DeptService {
42 43  
43 44 /**
44   - * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
45   - * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
46   - */
47   - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
48   -
49   - /**
50 45 * 部门缓存
51 46 * key:部门编号 {@link DeptDO#getId()}
52 47 *
53 48 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
54 49 */
55   - @SuppressWarnings("FieldCanBeLocal")
  50 + @Getter
56 51 private volatile Map<Long, DeptDO> deptCache;
57 52 /**
58 53 * 父部门缓存
... ... @@ -61,11 +56,8 @@ public class DeptServiceImpl implements DeptService {
61 56 *
62 57 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
63 58 */
  59 + @Getter
64 60 private volatile Multimap<Long, DeptDO> parentDeptCache;
65   - /**
66   - * 缓存部门的最大更新时间,用于后续的增量轮询,判断是否有更新
67   - */
68   - private volatile LocalDateTime maxUpdateTime;
69 61  
70 62 @Resource
71 63 private DeptMapper deptMapper;
... ... @@ -79,48 +71,21 @@ public class DeptServiceImpl implements DeptService {
79 71 @Override
80 72 @PostConstruct
81 73 public synchronized void initLocalCache() {
82   - initLocalCacheIfUpdate(null);
83   - }
84   -
85   - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
86   - public void schedulePeriodicRefresh() {
87   - initLocalCacheIfUpdate(this.maxUpdateTime);
88   - }
89   -
90   - /**
91   - * 刷新本地缓存
92   - *
93   - * @param maxUpdateTime 最大更新时间
94   - * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
95   - * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
96   - */
97   - private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
98 74 // 注意:忽略自动多租户,因为要全局初始化缓存
99 75 TenantUtils.executeIgnore(() -> {
100   - // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
101   - // 如果没有增量的数据变化,则不进行本地缓存的刷新
102   - if (maxUpdateTime != null
103   - && deptMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
104   - log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
105   - return;
106   - }
  76 + // 第一步:查询数据
107 77 List<DeptDO> depts = deptMapper.selectList();
108   - log.info("[initLocalCacheIfUpdate][缓存部门,数量为:{}]", depts.size());
  78 + log.info("[initLocalCache][缓存部门,数量为:{}]", depts.size());
109 79  
110   - // 第二步:构建缓存。创建或更新支付 Client
111   - // 构建缓存
  80 + // 第二步:构建缓存
112 81 ImmutableMap.Builder<Long, DeptDO> builder = ImmutableMap.builder();
113 82 ImmutableMultimap.Builder<Long, DeptDO> parentBuilder = ImmutableMultimap.builder();
114 83 depts.forEach(sysRoleDO -> {
115 84 builder.put(sysRoleDO.getId(), sysRoleDO);
116 85 parentBuilder.put(sysRoleDO.getParentId(), sysRoleDO);
117 86 });
118   - // 设置缓存
119 87 deptCache = builder.build();
120 88 parentDeptCache = parentBuilder.build();
121   -
122   - // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
123   - this.maxUpdateTime = CollectionUtils.getMaxValue(depts, DeptDO::getUpdateTime);
124 89 });
125 90 }
126 91  
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountCreateReqVO;
  5 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
  6 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  8 +
  9 +import javax.validation.Valid;
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * 邮箱账号 Service 接口
  14 + *
  15 + * @author wangjingyi
  16 + * @since 2022-03-21
  17 + */
  18 +public interface MailAccountService {
  19 +
  20 + /**
  21 + * 初始化邮箱账号的本地缓存
  22 + */
  23 + void initLocalCache();
  24 +
  25 + /**
  26 + * 从缓存中获取邮箱账号
  27 + *
  28 + * @param id 编号
  29 + * @return 邮箱账号
  30 + */
  31 + MailAccountDO getMailAccountFromCache(Long id);
  32 +
  33 + /**
  34 + * 创建邮箱账号
  35 + *
  36 + * @param createReqVO 邮箱账号信息
  37 + * @return 编号
  38 + */
  39 + Long createMailAccount(@Valid MailAccountCreateReqVO createReqVO);
  40 +
  41 + /**
  42 + * 修改邮箱账号
  43 + *
  44 + * @param updateReqVO 邮箱账号信息
  45 + */
  46 + void updateMailAccount(@Valid MailAccountUpdateReqVO updateReqVO);
  47 +
  48 + /**
  49 + * 删除邮箱账号
  50 + *
  51 + * @param id 编号
  52 + */
  53 + void deleteMailAccount(Long id);
  54 +
  55 + /**
  56 + * 获取邮箱账号信息
  57 + *
  58 + * @param id 编号
  59 + * @return 邮箱账号信息
  60 + */
  61 + MailAccountDO getMailAccount(Long id);
  62 +
  63 + /**
  64 + * 获取邮箱账号分页信息
  65 + *
  66 + * @param pageReqVO 邮箱账号分页参数
  67 + * @return 邮箱账号分页信息
  68 + */
  69 + PageResult<MailAccountDO> getMailAccountPage(MailAccountPageReqVO pageReqVO);
  70 +
  71 + /**
  72 + * 获取邮箱数组信息
  73 + *
  74 + * @return 邮箱账号信息数组
  75 + */
  76 + List<MailAccountDO> getMailAccountList();
  77 +
  78 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountCreateReqVO;
  5 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
  6 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO;
  7 +import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  9 +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
  10 +import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
  11 +import lombok.Getter;
  12 +import lombok.extern.slf4j.Slf4j;
  13 +import org.springframework.stereotype.Service;
  14 +import org.springframework.validation.annotation.Validated;
  15 +
  16 +import javax.annotation.PostConstruct;
  17 +import javax.annotation.Resource;
  18 +import java.util.List;
  19 +import java.util.Map;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  22 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  23 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
  24 +
  25 +/**
  26 + * 邮箱账号 Service 实现类
  27 + *
  28 + * @author wangjingyi
  29 + * @since 2022-03-21
  30 + */
  31 +@Service
  32 +@Validated
  33 +@Slf4j
  34 +public class MailAccountServiceImpl implements MailAccountService {
  35 +
  36 + @Resource
  37 + private MailAccountMapper mailAccountMapper;
  38 +
  39 + @Resource
  40 + private MailTemplateService mailTemplateService;
  41 +
  42 + @Resource
  43 + private MailProducer mailProducer;
  44 +
  45 + /**
  46 + * 邮箱账号缓存
  47 + * key:邮箱账号编码 {@link MailAccountDO#getId()}
  48 + *
  49 + * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
  50 + */
  51 + @Getter
  52 + private volatile Map<Long, MailAccountDO> mailAccountCache;
  53 +
  54 + @Override
  55 + @PostConstruct
  56 + public void initLocalCache() {
  57 + // 第一步:查询数据
  58 + List<MailAccountDO> accounts = mailAccountMapper.selectList();
  59 + log.info("[initLocalCache][缓存邮箱账号,数量:{}]", accounts.size());
  60 +
  61 + // 第二步:构建缓存
  62 + mailAccountCache = convertMap(accounts, MailAccountDO::getId);
  63 + }
  64 +
  65 + @Override
  66 + public MailAccountDO getMailAccountFromCache(Long id) {
  67 + return mailAccountCache.get(id);
  68 + }
  69 +
  70 + @Override
  71 + public Long createMailAccount(MailAccountCreateReqVO createReqVO) {
  72 + // 插入
  73 + MailAccountDO account = MailAccountConvert.INSTANCE.convert(createReqVO);
  74 + mailAccountMapper.insert(account);
  75 +
  76 + // 发送刷新消息
  77 + mailProducer.sendMailAccountRefreshMessage();
  78 + return account.getId();
  79 + }
  80 +
  81 + @Override
  82 + public void updateMailAccount(MailAccountUpdateReqVO updateReqVO) {
  83 + // 校验是否存在
  84 + validateMailAccountExists(updateReqVO.getId());
  85 +
  86 + // 更新
  87 + MailAccountDO updateObj = MailAccountConvert.INSTANCE.convert(updateReqVO);
  88 + mailAccountMapper.updateById(updateObj);
  89 + // 发送刷新消息
  90 + mailProducer.sendMailAccountRefreshMessage();
  91 + }
  92 +
  93 + @Override
  94 + public void deleteMailAccount(Long id) {
  95 + // 校验是否存在账号
  96 + validateMailAccountExists(id);
  97 + // 校验是否存在关联模版
  98 + if (mailTemplateService.countByAccountId(id) > 0) {
  99 + throw exception(MAIL_ACCOUNT_RELATE_TEMPLATE_EXISTS);
  100 + }
  101 +
  102 + // 删除
  103 + mailAccountMapper.deleteById(id);
  104 + // 发送刷新消息
  105 + mailProducer.sendMailAccountRefreshMessage();
  106 + }
  107 +
  108 + private void validateMailAccountExists(Long id) {
  109 + if (mailAccountMapper.selectById(id) == null) {
  110 + throw exception(MAIL_ACCOUNT_NOT_EXISTS);
  111 + }
  112 + }
  113 +
  114 + @Override
  115 + public MailAccountDO getMailAccount(Long id) {
  116 + return mailAccountMapper.selectById(id);
  117 + }
  118 +
  119 + @Override
  120 + public PageResult<MailAccountDO> getMailAccountPage(MailAccountPageReqVO pageReqVO) {
  121 + return mailAccountMapper.selectPage(pageReqVO);
  122 + }
  123 +
  124 + @Override
  125 + public List<MailAccountDO> getMailAccountList() {
  126 + return mailAccountMapper.selectList();
  127 + }
  128 +
  129 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
  5 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  6 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  8 +
  9 +import java.util.Map;
  10 +
  11 +/**
  12 + * 邮件日志 Service 接口
  13 + *
  14 + * @author wangjingyi
  15 + * @since 2022-03-21
  16 + */
  17 +public interface MailLogService {
  18 +
  19 + /**
  20 + * 邮件日志分页
  21 + *
  22 + * @param pageVO 分页参数
  23 + * @return 分页结果
  24 + */
  25 + PageResult<MailLogDO> getMailLogPage(MailLogPageReqVO pageVO);
  26 +
  27 + /**
  28 + * 获得指定编号的邮件日志
  29 + *
  30 + * @param id 日志编号
  31 + * @return 邮件日志
  32 + */
  33 + MailLogDO getMailLog(Long id);
  34 +
  35 + /**
  36 + * 创建邮件日志
  37 + *
  38 + * @param userId 用户编码
  39 + * @param userType 用户类型
  40 + * @param toMail 收件人邮件
  41 + * @param account 邮件账号信息
  42 + * @param template 模版信息
  43 + * @param templateContent 模版内容
  44 + * @param templateParams 模版参数
  45 + * @param isSend 是否发送成功
  46 + * @return 日志编号
  47 + */
  48 + Long createMailLog(Long userId,Integer userType, String toMail,
  49 + MailAccountDO account, MailTemplateDO template ,
  50 + String templateContent, Map<String, Object> templateParams, Boolean isSend);
  51 +
  52 + /**
  53 + * 更新邮件发送结果
  54 + *
  55 + * @param logId 日志编号
  56 + * @param messageId 发送后的消息编号
  57 + * @param exception 发送异常
  58 + */
  59 + void updateMailSendResult(Long logId, String messageId, Exception exception);
  60 +
  61 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
  5 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  6 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  8 +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper;
  9 +import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
  10 +import org.springframework.stereotype.Service;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import javax.annotation.Resource;
  14 +import java.util.Date;
  15 +import java.util.Map;
  16 +import java.util.Objects;
  17 +
  18 +import static cn.hutool.core.exceptions.ExceptionUtil.getRootCauseMessage;
  19 +
  20 +/**
  21 + * 邮件日志 Service 实现类
  22 + *
  23 + * @author wangjingyi
  24 + * @since 2022-03-21
  25 + */
  26 +@Service
  27 +@Validated
  28 +public class MailLogServiceImpl implements MailLogService {
  29 +
  30 + @Resource
  31 + private MailLogMapper mailLogMapper;
  32 +
  33 + @Override
  34 + public PageResult<MailLogDO> getMailLogPage(MailLogPageReqVO pageVO) {
  35 + return mailLogMapper.selectPage(pageVO);
  36 + }
  37 +
  38 + @Override
  39 + public MailLogDO getMailLog(Long id) {
  40 + return mailLogMapper.selectById(id);
  41 + }
  42 +
  43 + @Override
  44 + public Long createMailLog(Long userId, Integer userType, String toMail,
  45 + MailAccountDO account, MailTemplateDO template,
  46 + String templateContent, Map<String, Object> templateParams, Boolean isSend) {
  47 + MailLogDO.MailLogDOBuilder logDOBuilder = MailLogDO.builder();
  48 + // 根据是否要发送,设置状态
  49 + logDOBuilder.sendStatus(Objects.equals(isSend, true) ? MailSendStatusEnum.INIT.getStatus()
  50 + : MailSendStatusEnum.IGNORE.getStatus())
  51 + // 用户信息
  52 + .userId(userId).userType(userType).toMail(toMail)
  53 + .accountId(account.getId()).fromMail(account.getMail())
  54 + // 模板相关字段
  55 + .templateId(template.getId()).templateCode(template.getCode()).templateNickname(template.getNickname())
  56 + .templateTitle(template.getTitle()).templateContent(templateContent).templateParams(templateParams);
  57 +
  58 + // 插入数据库
  59 + MailLogDO logDO = logDOBuilder.build();
  60 + mailLogMapper.insert(logDO);
  61 + return logDO.getId();
  62 + }
  63 +
  64 + @Override
  65 + public void updateMailSendResult(Long logId, String messageId, Exception exception) {
  66 + // 1. 成功
  67 + if (exception == null) {
  68 + mailLogMapper.updateById(new MailLogDO().setId(logId).setSendTime(new Date())
  69 + .setSendStatus(MailSendStatusEnum.SUCCESS.getStatus()).setSendMessageId(messageId));
  70 + return;
  71 + }
  72 + // 2. 失败
  73 + mailLogMapper.updateById(new MailLogDO().setId(logId).setSendTime(new Date())
  74 + .setSendStatus(MailSendStatusEnum.FAILURE.getStatus()).setSendException(getRootCauseMessage(exception)));
  75 +
  76 + }
  77 +
  78 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
  4 +
  5 +import java.util.Map;
  6 +
  7 +/**
  8 + * 邮件发送 Service 接口
  9 + *
  10 + * @author wangjingyi
  11 + * @since 2022-03-21
  12 + */
  13 +public interface MailSendService {
  14 +
  15 + /**
  16 + * 发送单条邮件给管理后台的用户
  17 + *
  18 + * @param mail 邮箱
  19 + * @param userId 用户编码
  20 + * @param templateCode 邮件模版编码
  21 + * @param templateParams 邮件模版参数
  22 + * @return 发送日志编号
  23 + */
  24 + Long sendSingleMailToAdmin(String mail, Long userId,
  25 + String templateCode, Map<String, Object> templateParams);
  26 +
  27 + /**
  28 + * 发送单条邮件给用户 APP 的用户
  29 + *
  30 + * @param mail 邮箱
  31 + * @param userId 用户编码
  32 + * @param templateCode 邮件模版编码
  33 + * @param templateParams 邮件模版参数
  34 + * @return 发送日志编号
  35 + */
  36 + Long sendSingleMailToMember(String mail, Long userId,
  37 + String templateCode, Map<String, Object> templateParams);
  38 +
  39 + /**
  40 + * 发送单条邮件给用户
  41 + *
  42 + * @param mail 邮箱
  43 + * @param userId 用户编码
  44 + * @param userType 用户类型
  45 + * @param templateCode 邮件模版编码
  46 + * @param templateParams 邮件模版参数
  47 + * @return 发送日志编号
  48 + */
  49 + Long sendSingleMail(String mail, Long userId, Integer userType,
  50 + String templateCode, Map<String, Object> templateParams);
  51 +
  52 + /**
  53 + * 执行真正的邮件发送
  54 + * 注意,该方法仅仅提供给 MQ Consumer 使用
  55 + *
  56 + * @param message 邮件
  57 + */
  58 + void doSendMail(MailSendMessage message);
  59 +
  60 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.hutool.core.util.StrUtil;
  4 +import cn.hutool.extra.mail.MailAccount;
  5 +import cn.hutool.extra.mail.MailUtil;
  6 +import cn.iocoder.yudao.framework.common.core.KeyValue;
  7 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  8 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  9 +import cn.iocoder.yudao.module.system.convert.mail.MailAccountConvert;
  10 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  11 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  12 +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
  13 +import cn.iocoder.yudao.module.system.mq.message.mail.MailSendMessage;
  14 +import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
  15 +import cn.iocoder.yudao.module.system.service.member.MemberService;
  16 +import cn.iocoder.yudao.module.system.service.user.AdminUserService;
  17 +import com.google.common.annotations.VisibleForTesting;
  18 +import lombok.extern.slf4j.Slf4j;
  19 +import org.springframework.stereotype.Service;
  20 +import org.springframework.validation.annotation.Validated;
  21 +
  22 +import javax.annotation.Resource;
  23 +import java.util.List;
  24 +import java.util.Map;
  25 +import java.util.stream.Collectors;
  26 +
  27 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  28 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
  29 +
  30 +/**
  31 + * 邮箱模版 服务实现类
  32 + *
  33 + * @author wangjingyi
  34 + * @since 2022-03-21
  35 + */
  36 +@Service
  37 +@Validated
  38 +@Slf4j
  39 +public class MailSendServiceImpl implements MailSendService {
  40 +
  41 + @Resource
  42 + private AdminUserService adminUserService;
  43 + @Resource
  44 + private MemberService memberService;
  45 +
  46 + @Resource
  47 + private MailAccountService mailAccountService;
  48 + @Resource
  49 + private MailTemplateService mailTemplateService;
  50 +
  51 + @Resource
  52 + private MailLogService mailLogService;
  53 + @Resource
  54 + private MailProducer mailProducer;
  55 +
  56 + @Override
  57 + public Long sendSingleMailToAdmin(String mail, Long userId,
  58 + String templateCode, Map<String, Object> templateParams) {
  59 + // 如果 mail 为空,则加载用户编号对应的邮箱
  60 + if (StrUtil.isEmpty(mail)) {
  61 + AdminUserDO user = adminUserService.getUser(userId);
  62 + if (user != null) {
  63 + mail = user.getEmail();
  64 + }
  65 + }
  66 + // 执行发送
  67 + return sendSingleMail(mail, userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams);
  68 + }
  69 +
  70 + @Override
  71 + public Long sendSingleMailToMember(String mail, Long userId,
  72 + String templateCode, Map<String, Object> templateParams) {
  73 + // 如果 mail 为空,则加载用户编号对应的邮箱
  74 + if (StrUtil.isEmpty(mail)) {
  75 + mail = memberService.getMemberUserEmail(userId);
  76 + }
  77 + // 执行发送
  78 + return sendSingleMail(mail, userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams);
  79 + }
  80 +
  81 + @Override
  82 + public Long sendSingleMail(String mail, Long userId, Integer userType,
  83 + String templateCode, Map<String, Object> templateParams) {
  84 + // 校验邮箱模版是否合法
  85 + MailTemplateDO template = checkMailTemplateValid(templateCode);
  86 + // 校验邮箱账号是否合法
  87 + MailAccountDO account = checkMailAccountValid(template.getAccountId());
  88 +
  89 + // 校验邮箱是否存在
  90 + mail = checkMail(mail);
  91 + // 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
  92 + List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
  93 +
  94 + // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
  95 + Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus());
  96 + String content = mailTemplateService.formatMailTemplateContent(template.getContent(), templateParams);
  97 + Long sendLogId = mailLogService.createMailLog(userId, userType, mail,
  98 + account, template, content, templateParams, isSend);
  99 + // 发送 MQ 消息,异步执行发送短信
  100 + if (isSend) {
  101 + mailProducer.sendMailSendMessage(sendLogId, mail, account.getId(),
  102 + template.getNickname(), template.getTitle(), content);
  103 + }
  104 + return sendLogId;
  105 + }
  106 +
  107 + @Override
  108 + public void doSendMail(MailSendMessage message) {
  109 + // 1. 创建发送账号
  110 + MailAccountDO account = checkMailAccountValid(message.getAccountId());
  111 + MailAccount mailAccount = MailAccountConvert.INSTANCE.convert(account, message.getNickname());
  112 + // 2. 发送邮件
  113 + try {
  114 + String messageId = MailUtil.send(mailAccount, message.getMail(),
  115 + message.getTitle(), message.getContent(),true);
  116 + // 3. 更新结果(成功)
  117 + mailLogService.updateMailSendResult(message.getLogId(), messageId, null);
  118 + } catch (Exception e) {
  119 + // 3. 更新结果(异常)
  120 + mailLogService.updateMailSendResult(message.getLogId(), null, e);
  121 + }
  122 + }
  123 +
  124 + @VisibleForTesting
  125 + public MailTemplateDO checkMailTemplateValid(String templateCode) {
  126 + // 获得邮件模板。考虑到效率,从缓存中获取
  127 + MailTemplateDO template = mailTemplateService.getMailTemplateByCodeFromCache(templateCode);
  128 + // 邮件模板不存在
  129 + if (template == null) {
  130 + throw exception(MAIL_TEMPLATE_NOT_EXISTS);
  131 + }
  132 + return template;
  133 + }
  134 +
  135 + @VisibleForTesting
  136 + public MailAccountDO checkMailAccountValid(Long accountId) {
  137 + // 获得邮箱账号。考虑到效率,从缓存中获取
  138 + MailAccountDO account = mailAccountService.getMailAccountFromCache(accountId);
  139 + // 邮箱账号不存在
  140 + if (account == null) {
  141 + throw exception(MAIL_ACCOUNT_NOT_EXISTS);
  142 + }
  143 + return account;
  144 + }
  145 +
  146 + @VisibleForTesting
  147 + public String checkMail(String mail) {
  148 + if (StrUtil.isEmpty(mail)) {
  149 + throw exception(MAIL_SEND_MAIL_NOT_EXISTS);
  150 + }
  151 + return mail;
  152 + }
  153 +
  154 + /**
  155 + * 将参数模板,处理成有序的 KeyValue 数组
  156 + *
  157 + * @param template 邮箱模板
  158 + * @param templateParams 原始参数
  159 + * @return 处理后的参数
  160 + */
  161 + @VisibleForTesting
  162 + public List<KeyValue<String, Object>> buildTemplateParams(MailTemplateDO template, Map<String, Object> templateParams) {
  163 + return template.getParams().stream().map(key -> {
  164 + Object value = templateParams.get(key);
  165 + if (value == null) {
  166 + throw exception(MAIL_SEND_TEMPLATE_PARAM_MISS, key);
  167 + }
  168 + return new KeyValue<>(key, value);
  169 + }).collect(Collectors.toList());
  170 + }
  171 +
  172 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateCreateReqVO;
  5 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
  6 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateUpdateReqVO;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  8 +
  9 +import javax.validation.Valid;
  10 +import java.util.List;
  11 +import java.util.Map;
  12 +
  13 +/**
  14 + * 邮件模版 Service 接口
  15 + *
  16 + * @author wangjingyi
  17 + * @since 2022-03-21
  18 + */
  19 +public interface MailTemplateService {
  20 +
  21 + /**
  22 + * 初始化邮件模版的本地缓存
  23 + */
  24 + void initLocalCache();
  25 +
  26 + /**
  27 + * 邮件模版创建
  28 + *
  29 + * @param createReqVO 邮件信息
  30 + * @return 编号
  31 + */
  32 + Long createMailTemplate(@Valid MailTemplateCreateReqVO createReqVO);
  33 +
  34 + /**
  35 + * 邮件模版修改
  36 + *
  37 + * @param updateReqVO 邮件信息
  38 + */
  39 + void updateMailTemplate(@Valid MailTemplateUpdateReqVO updateReqVO);
  40 +
  41 + /**
  42 + * 邮件模版删除
  43 + *
  44 + * @param id 编号
  45 + */
  46 + void deleteMailTemplate(Long id);
  47 +
  48 + /**
  49 + * 获取邮件模版
  50 + *
  51 + * @param id 编号
  52 + * @return 邮件模版
  53 + */
  54 + MailTemplateDO getMailTemplate(Long id);
  55 +
  56 + /**
  57 + * 获取邮件模版分页
  58 + *
  59 + * @param pageReqVO 模版信息
  60 + * @return 邮件模版分页信息
  61 + */
  62 + PageResult<MailTemplateDO> getMailTemplatePage(MailTemplatePageReqVO pageReqVO);
  63 +
  64 + /**
  65 + * 获取邮件模板数组
  66 + *
  67 + * @return 模版数组
  68 + */
  69 + List<MailTemplateDO> getMailTemplateList();
  70 +
  71 + /**
  72 + * 从缓存中获取邮件模版
  73 + *
  74 + * @param code 模板编码
  75 + * @return 邮件模板
  76 + */
  77 + MailTemplateDO getMailTemplateByCodeFromCache(String code);
  78 +
  79 + /**
  80 + * 邮件模版内容合成
  81 + *
  82 + * @param content 邮件模版
  83 + * @param params 合成参数
  84 + * @return 格式化后的内容
  85 + */
  86 + String formatMailTemplateContent(String content, Map<String, Object> params);
  87 +
  88 + /**
  89 + * 获得指定邮件账号下的邮件模板数量
  90 + *
  91 + * @param accountId 账号编号
  92 + * @return 数量
  93 + */
  94 + long countByAccountId(Long accountId);
  95 +
  96 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.hutool.core.util.ObjUtil;
  4 +import cn.hutool.core.util.ReUtil;
  5 +import cn.hutool.core.util.StrUtil;
  6 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  7 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateCreateReqVO;
  8 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
  9 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateUpdateReqVO;
  10 +import cn.iocoder.yudao.module.system.convert.mail.MailTemplateConvert;
  11 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  12 +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
  13 +import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
  14 +import com.google.common.annotations.VisibleForTesting;
  15 +import lombok.Getter;
  16 +import lombok.extern.slf4j.Slf4j;
  17 +import org.springframework.stereotype.Service;
  18 +import org.springframework.validation.annotation.Validated;
  19 +
  20 +import javax.annotation.PostConstruct;
  21 +import javax.annotation.Resource;
  22 +import javax.validation.Valid;
  23 +import java.util.List;
  24 +import java.util.Map;
  25 +import java.util.regex.Pattern;
  26 +
  27 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  28 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  29 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
  30 +
  31 +/**
  32 + * 邮箱模版 Service 实现类
  33 + *
  34 + * @author wangjingyi
  35 + * @since 2022-03-21
  36 + */
  37 +@Service
  38 +@Validated
  39 +@Slf4j
  40 +public class MailTemplateServiceImpl implements MailTemplateService {
  41 +
  42 + /**
  43 + * 正则表达式,匹配 {} 中的变量
  44 + */
  45 + private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}");
  46 +
  47 + @Resource
  48 + private MailTemplateMapper mailTemplateMapper;
  49 +
  50 + @Resource
  51 + private MailProducer mailProducer;
  52 +
  53 + /**
  54 + * 邮件模板缓存
  55 + * key:邮件模版标识 {@link MailTemplateDO#getCode()}
  56 + *
  57 + * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
  58 + */
  59 + @Getter
  60 + private volatile Map<String, MailTemplateDO> mailTemplateCache;
  61 +
  62 + @Override
  63 + @PostConstruct
  64 + public void initLocalCache() {
  65 + // 第一步:查询数据
  66 + List<MailTemplateDO> templates = mailTemplateMapper.selectList();
  67 + log.info("[initLocalCache][缓存邮件模版,数量:{}]", templates.size());
  68 +
  69 + // 第二步:构建缓存
  70 + mailTemplateCache = convertMap(templates, MailTemplateDO::getCode);
  71 + }
  72 +
  73 + @Override
  74 + public Long createMailTemplate(MailTemplateCreateReqVO createReqVO) {
  75 + // 校验 code 是否唯一
  76 + validateCodeUnique(null, createReqVO.getCode());
  77 +
  78 + // 插入
  79 + MailTemplateDO template = MailTemplateConvert.INSTANCE.convert(createReqVO)
  80 + .setParams(parseTemplateContentParams(createReqVO.getContent()));
  81 + mailTemplateMapper.insert(template);
  82 + // 发送刷新消息
  83 + mailProducer.sendMailTemplateRefreshMessage();
  84 + return template.getId();
  85 + }
  86 +
  87 + @Override
  88 + public void updateMailTemplate(@Valid MailTemplateUpdateReqVO updateReqVO) {
  89 + // 校验是否存在
  90 + validateMailTemplateExists(updateReqVO.getId());
  91 + // 校验 code 是否唯一
  92 + validateCodeUnique(updateReqVO.getId(),updateReqVO.getCode());
  93 +
  94 + // 更新
  95 + MailTemplateDO updateObj = MailTemplateConvert.INSTANCE.convert(updateReqVO)
  96 + .setParams(parseTemplateContentParams(updateReqVO.getContent()));
  97 + mailTemplateMapper.updateById(updateObj);
  98 + // 发送刷新消息
  99 + mailProducer.sendMailTemplateRefreshMessage();
  100 + }
  101 +
  102 + @VisibleForTesting
  103 + public void validateCodeUnique(Long id, String code) {
  104 + MailTemplateDO template = mailTemplateMapper.selectByCode(code);
  105 + if (template == null) {
  106 + return;
  107 + }
  108 + // 存在 template 记录的情况下
  109 + if (id == null // 新增时,说明重复
  110 + || ObjUtil.notEqual(id, template.getId())) { // 更新时,如果 id 不一致,说明重复
  111 + throw exception(MAIL_TEMPLATE_CODE_EXISTS);
  112 + }
  113 + }
  114 +
  115 + @Override
  116 + public void deleteMailTemplate(Long id) {
  117 + // 校验是否存在
  118 + validateMailTemplateExists(id);
  119 +
  120 + // 删除
  121 + mailTemplateMapper.deleteById(id);
  122 + // 发送刷新消息
  123 + mailProducer.sendMailTemplateRefreshMessage();
  124 + }
  125 +
  126 + private void validateMailTemplateExists(Long id) {
  127 + if (mailTemplateMapper.selectById(id) == null) {
  128 + throw exception(MAIL_TEMPLATE_NOT_EXISTS);
  129 + }
  130 + }
  131 +
  132 + @Override
  133 + public MailTemplateDO getMailTemplate(Long id) {return mailTemplateMapper.selectById(id);}
  134 +
  135 + @Override
  136 + public PageResult<MailTemplateDO> getMailTemplatePage(MailTemplatePageReqVO pageReqVO) {
  137 + return mailTemplateMapper.selectPage(pageReqVO);
  138 + }
  139 +
  140 + @Override
  141 + public List<MailTemplateDO> getMailTemplateList() {return mailTemplateMapper.selectList();}
  142 +
  143 + @Override
  144 + public MailTemplateDO getMailTemplateByCodeFromCache(String code) {
  145 + return mailTemplateCache.get(code);
  146 + }
  147 +
  148 + @Override
  149 + public String formatMailTemplateContent(String content, Map<String, Object> params) {
  150 + return StrUtil.format(content, params);
  151 + }
  152 +
  153 + @VisibleForTesting
  154 + public List<String> parseTemplateContentParams(String content) {
  155 + return ReUtil.findAllGroup1(PATTERN_PARAMS, content);
  156 + }
  157 +
  158 + @Override
  159 + public long countByAccountId(Long accountId) {
  160 + return mailTemplateMapper.selectCountByAccountId(accountId);
  161 + }
  162 +
  163 +}
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberService.java
... ... @@ -15,4 +15,12 @@ public interface MemberService {
15 15 */
16 16 String getMemberUserMobile(Long id);
17 17  
  18 + /**
  19 + * 获得会员用户的邮箱
  20 + *
  21 + * @param id 会员用户编号
  22 + * @return 邮箱
  23 + */
  24 + String getMemberUserEmail(Long id);
  25 +
18 26 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/member/MemberServiceImpl.java
... ... @@ -21,14 +21,27 @@ public class MemberServiceImpl implements MemberService {
21 21  
22 22 @Override
23 23 public String getMemberUserMobile(Long id) {
24   - if (id == null) {
  24 + Object user = getMemberUser(id);
  25 + if (user == null) {
25 26 return null;
26 27 }
27   - Object user = ReflectUtil.invoke(getMemberUserApi(), "getUser", id);
  28 + return ReflectUtil.invoke(user, "getMobile");
  29 + }
  30 +
  31 + @Override
  32 + public String getMemberUserEmail(Long id) {
  33 + Object user = getMemberUser(id);
28 34 if (user == null) {
29 35 return null;
30 36 }
31   - return ReflectUtil.invoke(user, "getMobile");
  37 + return ReflectUtil.invoke(user, "getEmail");
  38 + }
  39 +
  40 + private Object getMemberUser(Long id) {
  41 + if (id == null) {
  42 + return null;
  43 + }
  44 + return ReflectUtil.invoke(getMemberUserApi(), "getUser", id);
32 45 }
33 46  
34 47 private Object getMemberUserApi() {
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImpl.java
... ... @@ -17,20 +17,17 @@ import com.google.common.annotations.VisibleForTesting;
17 17 import lombok.Getter;
18 18 import lombok.Setter;
19 19 import lombok.extern.slf4j.Slf4j;
20   -import org.springframework.scheduling.annotation.Scheduled;
21 20 import org.springframework.stereotype.Service;
22 21 import org.springframework.validation.annotation.Validated;
23 22  
24 23 import javax.annotation.PostConstruct;
25 24 import javax.annotation.Resource;
26   -import java.time.LocalDateTime;
27 25 import java.util.Collection;
28 26 import java.util.List;
29 27 import java.util.Map;
30 28  
31 29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
32 30 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
33   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
34 31 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
35 32  
36 33 /**
... ... @@ -44,12 +41,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
44 41 public class OAuth2ClientServiceImpl implements OAuth2ClientService {
45 42  
46 43 /**
47   - * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
48   - * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
49   - */
50   - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
51   -
52   - /**
53 44 * 客户端缓存
54 45 * key:客户端编号 {@link OAuth2ClientDO#getClientId()} ()}
55 46 *
... ... @@ -58,11 +49,6 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
58 49 @Getter // 解决单测
59 50 @Setter // 解决单测
60 51 private volatile Map<String, OAuth2ClientDO> clientCache;
61   - /**
62   - * 缓存角色的最大更新时间,用于后续的增量轮询,判断是否有更新
63   - */
64   - @Getter
65   - private volatile LocalDateTime maxUpdateTime;
66 52  
67 53 @Resource
68 54 private OAuth2ClientMapper oauth2ClientMapper;
... ... @@ -76,37 +62,12 @@ public class OAuth2ClientServiceImpl implements OAuth2ClientService {
76 62 @Override
77 63 @PostConstruct
78 64 public void initLocalCache() {
79   - initLocalCacheIfUpdate(null);
80   - }
81   -
82   - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
83   - public void schedulePeriodicRefresh() {
84   - initLocalCacheIfUpdate(this.maxUpdateTime);
85   - }
86   -
87   - /**
88   - * 刷新本地缓存
89   - *
90   - * @param maxUpdateTime 最大更新时间
91   - * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
92   - * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
93   - */
94   - private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
95   - // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
96   - // 如果没有增量的数据变化,则不进行本地缓存的刷新
97   - if (maxUpdateTime != null
98   - && oauth2ClientMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
99   - log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
100   - return;
101   - }
  65 + // 第一步:查询数据
102 66 List<OAuth2ClientDO> clients = oauth2ClientMapper.selectList();
103   - log.info("[initLocalCacheIfUpdate][缓存 OAuth2 客户端,数量为:{}]", clients.size());
  67 + log.info("[initLocalCache][缓存 OAuth2 客户端,数量为:{}]", clients.size());
104 68  
105 69 // 第二步:构建缓存。
106 70 clientCache = convertMap(clients, OAuth2ClientDO::getClientId);
107   -
108   - // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
109   - this.maxUpdateTime = getMaxValue(clients, OAuth2ClientDO::getUpdateTime);
110 71 }
111 72  
112 73 @Override
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceImpl.java
... ... @@ -18,9 +18,9 @@ import com.google.common.annotations.VisibleForTesting;
18 18 import com.google.common.collect.ImmutableMap;
19 19 import com.google.common.collect.ImmutableMultimap;
20 20 import com.google.common.collect.Multimap;
  21 +import lombok.Getter;
21 22 import lombok.extern.slf4j.Slf4j;
22 23 import org.springframework.context.annotation.Lazy;
23   -import org.springframework.scheduling.annotation.Scheduled;
24 24 import org.springframework.stereotype.Service;
25 25 import org.springframework.transaction.annotation.Transactional;
26 26 import org.springframework.transaction.support.TransactionSynchronization;
... ... @@ -28,7 +28,6 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
28 28  
29 29 import javax.annotation.PostConstruct;
30 30 import javax.annotation.Resource;
31   -import java.time.LocalDateTime;
32 31 import java.util.*;
33 32 import java.util.stream.Collectors;
34 33  
... ... @@ -44,17 +43,12 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
44 43 public class MenuServiceImpl implements MenuService {
45 44  
46 45 /**
47   - * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
48   - * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
49   - */
50   - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
51   -
52   - /**
53 46 * 菜单缓存
54 47 * key:菜单编号
55 48 *
56 49 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
57 50 */
  51 + @Getter
58 52 private volatile Map<Long, MenuDO> menuCache;
59 53 /**
60 54 * 权限与菜单缓存
... ... @@ -63,11 +57,8 @@ public class MenuServiceImpl implements MenuService {
63 57 *
64 58 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
65 59 */
  60 + @Getter
66 61 private volatile Multimap<String, MenuDO> permissionMenuCache;
67   - /**
68   - * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
69   - */
70   - private volatile LocalDateTime maxUpdateTime;
71 62  
72 63 @Resource
73 64 private MenuMapper menuMapper;
... ... @@ -86,33 +77,11 @@ public class MenuServiceImpl implements MenuService {
86 77 @Override
87 78 @PostConstruct
88 79 public synchronized void initLocalCache() {
89   - initLocalCacheIfUpdate(null);
90   - }
91   -
92   - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
93   - public void schedulePeriodicRefresh() {
94   - initLocalCacheIfUpdate(this.maxUpdateTime);
95   - }
96   -
97   - /**
98   - * 刷新本地缓存
99   - *
100   - * @param maxUpdateTime 最大更新时间
101   - * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
102   - * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
103   - */
104   - private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
105   - // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
106   - // 如果没有增量的数据变化,则不进行本地缓存的刷新
107   - if (maxUpdateTime != null
108   - && menuMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
109   - log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
110   - return;
111   - }
  80 + // 第一步:查询数据
112 81 List<MenuDO> menuList = menuMapper.selectList();
113   - log.info("[initLocalCacheIfUpdate][缓存菜单,数量为:{}]", menuList.size());
  82 + log.info("[initLocalCache][缓存菜单,数量为:{}]", menuList.size());
114 83  
115   - // 第二步:构建缓存。
  84 + // 第二步:构建缓存
116 85 ImmutableMap.Builder<Long, MenuDO> menuCacheBuilder = ImmutableMap.builder();
117 86 ImmutableMultimap.Builder<String, MenuDO> permMenuCacheBuilder = ImmutableMultimap.builder();
118 87 menuList.forEach(menuDO -> {
... ... @@ -123,9 +92,6 @@ public class MenuServiceImpl implements MenuService {
123 92 });
124 93 menuCache = menuCacheBuilder.build();
125 94 permissionMenuCache = permMenuCacheBuilder.build();
126   -
127   - // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
128   - this.maxUpdateTime = CollectionUtils.getMaxValue(menuList, MenuDO::getUpdateTime);
129 95 }
130 96  
131 97 @Override
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java
... ... @@ -16,9 +16,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
16 16 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
17 17 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
18 18 import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
19   -import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuBatchInsertMapper;
20 19 import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
21   -import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleBatchInsertMapper;
22 20 import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
23 21 import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
24 22 import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
... ... @@ -32,7 +30,6 @@ import com.google.common.collect.Sets;
32 30 import lombok.Getter;
33 31 import lombok.Setter;
34 32 import lombok.extern.slf4j.Slf4j;
35   -import org.springframework.scheduling.annotation.Scheduled;
36 33 import org.springframework.stereotype.Service;
37 34 import org.springframework.transaction.annotation.Transactional;
38 35 import org.springframework.transaction.support.TransactionSynchronization;
... ... @@ -40,12 +37,10 @@ import org.springframework.transaction.support.TransactionSynchronizationManager
40 37  
41 38 import javax.annotation.PostConstruct;
42 39 import javax.annotation.Resource;
43   -import java.time.LocalDateTime;
44 40 import java.util.*;
45 41 import java.util.function.Supplier;
46 42  
47 43 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
48   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
49 44 import static java.util.Collections.singleton;
50 45  
51 46 /**
... ... @@ -58,12 +53,6 @@ import static java.util.Collections.singleton;
58 53 public class PermissionServiceImpl implements PermissionService {
59 54  
60 55 /**
61   - * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
62   - * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
63   - */
64   - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
65   -
66   - /**
67 56 * 角色编号与菜单编号的缓存映射
68 57 * key:角色编号
69 58 * value:菜单编号的数组
... ... @@ -83,11 +72,6 @@ public class PermissionServiceImpl implements PermissionService {
83 72 @Getter
84 73 @Setter // 单元测试需要
85 74 private volatile Multimap<Long, Long> menuRoleCache;
86   - /**
87   - * 缓存 RoleMenu 的最大更新时间,用于后续的增量轮询,判断是否有更新
88   - */
89   - @Getter
90   - private volatile LocalDateTime roleMenuMaxUpdateTime;
91 75  
92 76 /**
93 77 * 用户编号与角色编号的缓存映射
... ... @@ -99,20 +83,11 @@ public class PermissionServiceImpl implements PermissionService {
99 83 @Getter
100 84 @Setter // 单元测试需要
101 85 private volatile Map<Long, Set<Long>> userRoleCache;
102   - /**
103   - * 缓存 UserRole 的最大更新时间,用于后续的增量轮询,判断是否有更新
104   - */
105   - @Getter
106   - private volatile LocalDateTime userRoleMaxUpdateTime;
107 86  
108 87 @Resource
109 88 private RoleMenuMapper roleMenuMapper;
110 89 @Resource
111   - private RoleMenuBatchInsertMapper roleMenuBatchInsertMapper;
112   - @Resource
113 90 private UserRoleMapper userRoleMapper;
114   - @Resource
115   - private UserRoleBatchInsertMapper userRoleBatchInsertMapper;
116 91  
117 92 @Resource
118 93 private RoleService roleService;
... ... @@ -129,38 +104,22 @@ public class PermissionServiceImpl implements PermissionService {
129 104 @Override
130 105 @PostConstruct
131 106 public void initLocalCache() {
132   - initLocalCacheIfUpdateForRoleMenu(null);
133   - initLocalCacheIfUpdateForUserRole(null);
134   - }
135   -
136   - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
137   - public void schedulePeriodicRefresh() {
138   - initLocalCacheIfUpdateForRoleMenu(this.roleMenuMaxUpdateTime);
139   - initLocalCacheIfUpdateForUserRole(this.userRoleMaxUpdateTime);
  107 + initLocalCacheForRoleMenu();
  108 + initLocalCacheForUserRole();
140 109 }
141 110  
142 111 /**
143 112 * 刷新 RoleMenu 本地缓存
144   - *
145   - * @param maxUpdateTime 最大更新时间
146   - * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
147   - * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
148 113 */
149 114 @VisibleForTesting
150   - void initLocalCacheIfUpdateForRoleMenu(LocalDateTime maxUpdateTime) {
  115 + void initLocalCacheForRoleMenu() {
151 116 // 注意:忽略自动多租户,因为要全局初始化缓存
152 117 TenantUtils.executeIgnore(() -> {
153   - // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
154   - // 如果没有增量的数据变化,则不进行本地缓存的刷新
155   - if (maxUpdateTime != null
156   - && roleMenuMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
157   - log.info("[initLocalCacheIfUpdateForRoleMenu][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
158   - return;
159   - }
  118 + // 第一步:查询数据
160 119 List<RoleMenuDO> roleMenus = roleMenuMapper.selectList();
161   - log.info("[initLocalCacheIfUpdateForRoleMenu][缓存角色与菜单,数量为:{}]", roleMenus.size());
  120 + log.info("[initLocalCacheForRoleMenu][缓存角色与菜单,数量为:{}]", roleMenus.size());
162 121  
163   - // 第二步:构建缓存。
  122 + // 第二步:构建缓存
164 123 ImmutableMultimap.Builder<Long, Long> roleMenuCacheBuilder = ImmutableMultimap.builder();
165 124 ImmutableMultimap.Builder<Long, Long> menuRoleCacheBuilder = ImmutableMultimap.builder();
166 125 roleMenus.forEach(roleMenuDO -> {
... ... @@ -169,40 +128,24 @@ public class PermissionServiceImpl implements PermissionService {
169 128 });
170 129 roleMenuCache = roleMenuCacheBuilder.build();
171 130 menuRoleCache = menuRoleCacheBuilder.build();
172   -
173   - // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
174   - this.roleMenuMaxUpdateTime = getMaxValue(roleMenus, RoleMenuDO::getUpdateTime);
175 131 });
176 132 }
177 133  
178 134 /**
179 135 * 刷新 UserRole 本地缓存
180   - *
181   - * @param maxUpdateTime 最大更新时间
182   - * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
183   - * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
184 136 */
185 137 @VisibleForTesting
186   - void initLocalCacheIfUpdateForUserRole(LocalDateTime maxUpdateTime) {
  138 + void initLocalCacheForUserRole() {
187 139 // 注意:忽略自动多租户,因为要全局初始化缓存
188 140 TenantUtils.executeIgnore(() -> {
189   - // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
190   - // 如果没有增量的数据变化,则不进行本地缓存的刷新
191   - if (maxUpdateTime != null
192   - && userRoleMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
193   - log.info("[initLocalCacheIfUpdateForUserRole][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
194   - return;
195   - }
  141 + // 第一步:加载数据
196 142 List<UserRoleDO> userRoles = userRoleMapper.selectList();
197   - log.info("[initLocalCacheIfUpdateForUserRole][缓存用户与角色,数量为:{}]", userRoles.size());
  143 + log.info("[initLocalCacheForUserRole][缓存用户与角色,数量为:{}]", userRoles.size());
198 144  
199 145 // 第二步:构建缓存。
200 146 ImmutableMultimap.Builder<Long, Long> userRoleCacheBuilder = ImmutableMultimap.builder();
201 147 userRoles.forEach(userRoleDO -> userRoleCacheBuilder.put(userRoleDO.getUserId(), userRoleDO.getRoleId()));
202 148 userRoleCache = CollectionUtils.convertMultiMap2(userRoles, UserRoleDO::getUserId, UserRoleDO::getRoleId);
203   -
204   - // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
205   - this.userRoleMaxUpdateTime = getMaxValue(userRoles, UserRoleDO::getUpdateTime);
206 149 });
207 150 }
208 151  
... ... @@ -264,7 +207,7 @@ public class PermissionServiceImpl implements PermissionService {
264 207 Collection<Long> deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIds);
265 208 // 执行新增和删除。对于已经授权的菜单,不用做任何处理
266 209 if (!CollectionUtil.isEmpty(createMenuIds)) {
267   - roleMenuBatchInsertMapper.saveBatch(CollectionUtils.convertList(createMenuIds, menuId -> {
  210 + roleMenuMapper.insertBatch(CollectionUtils.convertList(createMenuIds, menuId -> {
268 211 RoleMenuDO entity = new RoleMenuDO();
269 212 entity.setRoleId(roleId);
270 213 entity.setMenuId(menuId);
... ... @@ -308,7 +251,7 @@ public class PermissionServiceImpl implements PermissionService {
308 251 Collection<Long> deleteMenuIds = CollUtil.subtract(dbRoleIds, roleIds);
309 252 // 执行新增和删除。对于已经授权的角色,不用做任何处理
310 253 if (!CollectionUtil.isEmpty(createRoleIds)) {
311   - userRoleBatchInsertMapper.saveBatch(CollectionUtils.convertList(createRoleIds, roleId -> {
  254 + userRoleMapper.insertBatch(CollectionUtils.convertList(createRoleIds, roleId -> {
312 255 UserRoleDO entity = new UserRoleDO();
313 256 entity.setUserId(userId);
314 257 entity.setRoleId(roleId);
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java
... ... @@ -22,7 +22,6 @@ import com.google.common.annotations.VisibleForTesting;
22 22 import lombok.Getter;
23 23 import lombok.extern.slf4j.Slf4j;
24 24 import org.springframework.lang.Nullable;
25   -import org.springframework.scheduling.annotation.Scheduled;
26 25 import org.springframework.stereotype.Service;
27 26 import org.springframework.transaction.annotation.Transactional;
28 27 import org.springframework.transaction.support.TransactionSynchronization;
... ... @@ -31,7 +30,6 @@ import org.springframework.util.StringUtils;
31 30  
32 31 import javax.annotation.PostConstruct;
33 32 import javax.annotation.Resource;
34   -import java.time.LocalDateTime;
35 33 import java.util.*;
36 34 import java.util.stream.Collectors;
37 35  
... ... @@ -48,12 +46,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
48 46 public class RoleServiceImpl implements RoleService {
49 47  
50 48 /**
51   - * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
52   - * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
53   - */
54   - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
55   -
56   - /**
57 49 * 角色缓存
58 50 * key:角色编号 {@link RoleDO#getId()}
59 51 *
... ... @@ -61,11 +53,6 @@ public class RoleServiceImpl implements RoleService {
61 53 */
62 54 @Getter
63 55 private volatile Map<Long, RoleDO> roleCache;
64   - /**
65   - * 缓存角色的最大更新时间,用于后续的增量轮询,判断是否有更新
66   - */
67   - @Getter
68   - private volatile LocalDateTime maxUpdateTime;
69 56  
70 57 @Resource
71 58 private PermissionService permissionService;
... ... @@ -82,39 +69,14 @@ public class RoleServiceImpl implements RoleService {
82 69 @Override
83 70 @PostConstruct
84 71 public void initLocalCache() {
85   - initLocalCacheIfUpdate(null);
86   - }
87   -
88   - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
89   - public void schedulePeriodicRefresh() {
90   - initLocalCacheIfUpdate(this.maxUpdateTime);
91   - }
92   -
93   - /**
94   - * 刷新本地缓存
95   - *
96   - * @param maxUpdateTime 最大更新时间
97   - * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
98   - * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
99   - */
100   - private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
101 72 // 注意:忽略自动多租户,因为要全局初始化缓存
102 73 TenantUtils.executeIgnore(() -> {
103   - // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
104   - // 如果没有增量的数据变化,则不进行本地缓存的刷新
105   - if (maxUpdateTime != null
106   - && roleMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
107   - log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
108   - return;
109   - }
  74 + // 第一步:查询数据
110 75 List<RoleDO> roleList = roleMapper.selectList();
111   - log.info("[initLocalCacheIfUpdate][缓存角色,数量为:{}]", roleList.size());
  76 + log.info("[initLocalCache][缓存角色,数量为:{}]", roleList.size());
112 77  
113   - // 第二步:构建缓存。
  78 + // 第二步:构建缓存
114 79 roleCache = CollectionUtils.convertMap(roleList, RoleDO::getId);
115   -
116   - // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
117   - this.maxUpdateTime = CollectionUtils.getMaxValue(roleList, RoleDO::getUpdateTime);
118 80 });
119 81 }
120 82  
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java
... ... @@ -17,13 +17,11 @@ import com.google.common.collect.HashMultimap;
17 17 import com.google.common.collect.Multimap;
18 18 import lombok.Getter;
19 19 import lombok.extern.slf4j.Slf4j;
20   -import org.springframework.scheduling.annotation.Scheduled;
21 20 import org.springframework.stereotype.Service;
22 21 import org.springframework.validation.annotation.Validated;
23 22  
24 23 import javax.annotation.PostConstruct;
25 24 import javax.annotation.Resource;
26   -import java.time.LocalDateTime;
27 25 import java.util.*;
28 26  
29 27 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -41,12 +39,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_
41 39 public class SensitiveWordServiceImpl implements SensitiveWordService {
42 40  
43 41 /**
44   - * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
45   - * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
46   - */
47   - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
48   -
49   - /**
50 42 * 敏感词标签缓存
51 43 * key:敏感词编号 {@link SensitiveWordDO#getId()}
52 44 * <p>
... ... @@ -55,12 +47,6 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
55 47 @Getter
56 48 private volatile Set<String> sensitiveWordTagsCache = Collections.emptySet();
57 49  
58   - /**
59   - * 缓存敏感词的最大更新时间,用于后续的增量轮询,判断是否有更新
60   - */
61   - @Getter
62   - private volatile LocalDateTime maxUpdateTime;
63   -
64 50 @Resource
65 51 private SensitiveWordMapper sensitiveWordMapper;
66 52  
... ... @@ -84,42 +70,17 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
84 70 @Override
85 71 @PostConstruct
86 72 public void initLocalCache() {
87   - initLocalCacheIfUpdate(null);
88   - }
89   -
90   - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
91   - public void schedulePeriodicRefresh() {
92   - initLocalCacheIfUpdate(this.maxUpdateTime);
93   - }
94   -
95   - /**
96   - * 刷新本地缓存
97   - *
98   - * @param maxUpdateTime 最大更新时间
99   - * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
100   - * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
101   - */
102   - private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
103   - // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
104   - // 如果没有增量的数据变化,则不进行本地缓存的刷新
105   - if (maxUpdateTime != null
106   - && sensitiveWordMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
107   - log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
108   - return;
109   - }
  73 + // 第一步:查询数据
110 74 List<SensitiveWordDO> sensitiveWords = sensitiveWordMapper.selectList();
111   - log.info("[initLocalCacheIfUpdate][缓存敏感词,数量为:{}]", sensitiveWords.size());
  75 + log.info("[initLocalCache][缓存敏感词,数量为:{}]", sensitiveWords.size());
112 76  
113   - // 第二步:构建缓存。
  77 + // 第二步:构建缓存
114 78 // 写入 sensitiveWordTagsCache 缓存
115 79 Set<String> tags = new HashSet<>();
116 80 sensitiveWords.forEach(word -> tags.addAll(word.getTags()));
117 81 sensitiveWordTagsCache = tags;
118 82 // 写入 defaultSensitiveWordTrie、tagSensitiveWordTries 缓存
119 83 initSensitiveWordTrie(sensitiveWords);
120   -
121   - // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
122   - this.maxUpdateTime = CollectionUtils.getMaxValue(sensitiveWords, SensitiveWordDO::getUpdateTime);
123 84 }
124 85  
125 86 private void initSensitiveWordTrie(List<SensitiveWordDO> wordDOs) {
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java
... ... @@ -11,17 +11,14 @@ import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
11 11 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
12 12 import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
13 13 import lombok.extern.slf4j.Slf4j;
14   -import org.springframework.scheduling.annotation.Scheduled;
15 14 import org.springframework.stereotype.Service;
16 15  
17 16 import javax.annotation.PostConstruct;
18 17 import javax.annotation.Resource;
19   -import java.time.LocalDateTime;
20 18 import java.util.Collection;
21 19 import java.util.List;
22 20  
23 21 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
24   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
25 22 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
26 23 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
27 24  
... ... @@ -34,17 +31,6 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNE
34 31 @Slf4j
35 32 public class SmsChannelServiceImpl implements SmsChannelService {
36 33  
37   - /**
38   - * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
39   - * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
40   - */
41   - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
42   -
43   - /**
44   - * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新
45   - */
46   - private volatile LocalDateTime maxUpdateTime;
47   -
48 34 @Resource
49 35 private SmsClientFactory smsClientFactory;
50 36  
... ... @@ -60,38 +46,13 @@ public class SmsChannelServiceImpl implements SmsChannelService {
60 46 @Override
61 47 @PostConstruct
62 48 public void initLocalCache() {
63   - initLocalCacheIfUpdate(null);
64   - }
65   -
66   - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
67   - public void schedulePeriodicRefresh() {
68   - initLocalCacheIfUpdate(this.maxUpdateTime);
69   - }
70   -
71   - /**
72   - * 刷新本地缓存
73   - *
74   - * @param maxUpdateTime 最大更新时间
75   - * 1. 如果 maxUpdateTime 为 null,则“强制”刷新缓存
76   - * 2. 如果 maxUpdateTime 不为 null,判断自 maxUpdateTime 是否有数据发生变化,有的情况下才刷新缓存
77   - */
78   - private void initLocalCacheIfUpdate(LocalDateTime maxUpdateTime) {
79   - // 第一步:基于 maxUpdateTime 判断缓存是否刷新。
80   - // 如果没有增量的数据变化,则不进行本地缓存的刷新
81   - if (maxUpdateTime != null
82   - && smsChannelMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
83   - log.info("[initLocalCacheIfUpdate][数据未发生变化({}),本地缓存不刷新]", maxUpdateTime);
84   - return;
85   - }
  49 + // 第一步:查询数据
86 50 List<SmsChannelDO> channels = smsChannelMapper.selectList();
87   - log.info("[initLocalCacheIfUpdate][缓存短信渠道,数量为:{}]", channels.size());
  51 + log.info("[initLocalCache][缓存短信渠道,数量为:{}]", channels.size());
88 52  
89   - // 第二步:构建缓存。创建或更新短信 Client
  53 + // 第二步:构建缓存:创建或更新短信 Client
90 54 List<SmsChannelProperties> propertiesList = SmsChannelConvert.INSTANCE.convertList02(channels);
91 55 propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties));
92   -
93   - // 第三步:设置最新的 maxUpdateTime,用于下次的增量判断。
94   - this.maxUpdateTime = getMaxValue(channels, SmsChannelDO::getUpdateTime);
95 56 }
96 57  
97 58 @Override
... ... @@ -120,7 +81,7 @@ public class SmsChannelServiceImpl implements SmsChannelService {
120 81 public void deleteSmsChannel(Long id) {
121 82 // 校验存在
122 83 this.validateSmsChannelExists(id);
123   - // 校验是否有字典数据
  84 + // 校验是否有在使用该账号的模版
124 85 if (smsTemplateService.countByChannelId(id) > 0) {
125 86 throw exception(SMS_CHANNEL_HAS_CHILDREN);
126 87 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java
1 1 package cn.iocoder.yudao.module.system.service.sms;
2 2  
3   -import cn.hutool.core.collection.CollUtil;
4 3 import cn.hutool.core.util.ReUtil;
5 4 import cn.hutool.core.util.StrUtil;
6 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
... ... @@ -20,15 +19,17 @@ import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
20 19 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsTemplateMapper;
21 20 import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
22 21 import com.google.common.annotations.VisibleForTesting;
  22 +import lombok.Getter;
23 23 import lombok.extern.slf4j.Slf4j;
24   -import org.springframework.scheduling.annotation.Scheduled;
25 24 import org.springframework.stereotype.Service;
26 25 import org.springframework.util.Assert;
27 26  
28 27 import javax.annotation.PostConstruct;
29 28 import javax.annotation.Resource;
30   -import java.time.LocalDateTime;
31   -import java.util.*;
  29 +import java.util.Collection;
  30 +import java.util.List;
  31 +import java.util.Map;
  32 +import java.util.Objects;
32 33 import java.util.regex.Pattern;
33 34  
34 35 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -38,19 +39,13 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
38 39 * 短信模板 Service 实现类
39 40 *
40 41 * @author zzf
41   - * @date 2021/1/25 9:25
  42 + * @since 2021/1/25 9:25
42 43 */
43 44 @Service
44 45 @Slf4j
45 46 public class SmsTemplateServiceImpl implements SmsTemplateService {
46 47  
47 48 /**
48   - * 定时执行 {@link #schedulePeriodicRefresh()} 的周期
49   - * 因为已经通过 Redis Pub/Sub 机制,所以频率不需要高
50   - */
51   - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;
52   -
53   - /**
54 49 * 正则表达式,匹配 {} 中的变量
55 50 */
56 51 private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}");
... ... @@ -73,51 +68,18 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
73 68 *
74 69 * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向
75 70 */
  71 + @Getter // 为了方便测试,这里提供 getter 方法
76 72 private volatile Map<String, SmsTemplateDO> smsTemplateCache;
77   - /**
78   - * 缓存短信模板的最大更新时间,用于后续的增量轮询,判断是否有更新
79   - */
80   - private volatile LocalDateTime maxUpdateTime;
81 73  
82 74 @Override
83 75 @PostConstruct
84 76 public void initLocalCache() {
85   - // 获取短信模板列表,如果有更新
86   - List<SmsTemplateDO> smsTemplateList = this.loadSmsTemplateIfUpdate(maxUpdateTime);
87   - if (CollUtil.isEmpty(smsTemplateList)) {
88   - return;
89   - }
  77 + // 第一步:查询数据
  78 + List<SmsTemplateDO> smsTemplateList = smsTemplateMapper.selectList();
  79 + log.info("[initLocalCache][缓存短信模版,数量为:{}]", smsTemplateList.size());
90 80  
91   - // 写入缓存
  81 + // 第二步:构建缓存
92 82 smsTemplateCache = CollectionUtils.convertMap(smsTemplateList, SmsTemplateDO::getCode);
93   - maxUpdateTime = CollectionUtils.getMaxValue(smsTemplateList, SmsTemplateDO::getUpdateTime);
94   - log.info("[initLocalCache][初始化 SmsTemplate 数量为 {}]", smsTemplateList.size());
95   - }
96   -
97   - /**
98   - * 如果短信模板发生变化,从数据库中获取最新的全量短信模板。
99   - * 如果未发生变化,则返回空
100   - *
101   - * @param maxUpdateTime 当前短信模板的最大更新时间
102   - * @return 短信模板列表
103   - */
104   - private List<SmsTemplateDO> loadSmsTemplateIfUpdate(LocalDateTime maxUpdateTime) {
105   - // 第一步,判断是否要更新。
106   - if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据
107   - log.info("[loadSmsTemplateIfUpdate][首次加载全量短信模板]");
108   - } else { // 判断数据库中是否有更新的短信模板
109   - if (smsTemplateMapper.selectCountByUpdateTimeGt(maxUpdateTime) == 0) {
110   - return null;
111   - }
112   - log.info("[loadSmsTemplateIfUpdate][增量加载全量短信模板]");
113   - }
114   - // 第二步,如果有更新,则从数据库加载所有短信模板
115   - return smsTemplateMapper.selectList();
116   - }
117   -
118   - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
119   - public void schedulePeriodicRefresh() {
120   - initLocalCache();
121 83 }
122 84  
123 85 @Override
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceTest.java
1 1 package cn.iocoder.yudao.module.system.service.dept;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
  5 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
4 6 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
  7 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
5 8 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO;
6 9 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO;
7 10 import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO;
... ... @@ -9,9 +12,6 @@ import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
9 12 import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper;
10 13 import cn.iocoder.yudao.module.system.enums.dept.DeptIdEnum;
11 14 import cn.iocoder.yudao.module.system.mq.producer.dept.DeptProducer;
12   -import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
13   -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
14   -import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
15 15 import com.google.common.collect.Multimap;
16 16 import org.junit.jupiter.api.BeforeEach;
17 17 import org.junit.jupiter.api.Test;
... ... @@ -19,17 +19,15 @@ import org.springframework.boot.test.mock.mockito.MockBean;
19 19 import org.springframework.context.annotation.Import;
20 20  
21 21 import javax.annotation.Resource;
22   -import java.time.LocalDateTime;
23 22 import java.util.List;
24 23 import java.util.Map;
25 24 import java.util.function.Consumer;
26 25  
27   -import static cn.hutool.core.bean.BeanUtil.getFieldValue;
28 26 import static cn.hutool.core.util.RandomUtil.randomEle;
29   -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
30 27 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
31 28 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
32 29 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  30 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
33 31 import static org.junit.jupiter.api.Assertions.*;
34 32 import static org.mockito.Mockito.times;
35 33 import static org.mockito.Mockito.verify;
... ... @@ -56,8 +54,7 @@ public class DeptServiceTest extends BaseDbUnitTest {
56 54 }
57 55  
58 56 @Test
59   - @SuppressWarnings("unchecked")
60   - void testInitLocalCache() {
  57 + public void testInitLocalCache() {
61 58 // mock 数据
62 59 DeptDO deptDO1 = randomDeptDO();
63 60 deptMapper.insert(deptDO1);
... ... @@ -67,18 +64,15 @@ public class DeptServiceTest extends BaseDbUnitTest {
67 64 // 调用
68 65 deptService.initLocalCache();
69 66 // 断言 deptCache 缓存
70   - Map<Long, DeptDO> deptCache = (Map<Long, DeptDO>) getFieldValue(deptService, "deptCache");
  67 + Map<Long, DeptDO> deptCache = deptService.getDeptCache();
71 68 assertEquals(2, deptCache.size());
72 69 assertPojoEquals(deptDO1, deptCache.get(deptDO1.getId()));
73 70 assertPojoEquals(deptDO2, deptCache.get(deptDO2.getId()));
74 71 // 断言 parentDeptCache 缓存
75   - Multimap<Long, DeptDO> parentDeptCache = (Multimap<Long, DeptDO>) getFieldValue(deptService, "parentDeptCache");
  72 + Multimap<Long, DeptDO> parentDeptCache = deptService.getParentDeptCache();
76 73 assertEquals(2, parentDeptCache.size());
77 74 assertPojoEquals(deptDO1, parentDeptCache.get(deptDO1.getParentId()));
78 75 assertPojoEquals(deptDO2, parentDeptCache.get(deptDO2.getParentId()));
79   - // 断言 maxUpdateTime 缓存
80   - LocalDateTime maxUpdateTime = (LocalDateTime) getFieldValue(deptService, "maxUpdateTime");
81   - assertEquals(ObjectUtils.max(deptDO1.getUpdateTime(), deptDO2.getUpdateTime()), maxUpdateTime);
82 76 }
83 77  
84 78 @Test
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailAccountServiceImplTest.java 0 → 100755
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  5 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountCreateReqVO;
  6 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountPageReqVO;
  7 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.account.MailAccountUpdateReqVO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  9 +import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
  10 +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailAccountMapper;
  11 +import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
  12 +import org.junit.jupiter.api.Test;
  13 +import org.springframework.boot.test.mock.mockito.MockBean;
  14 +import org.springframework.context.annotation.Import;
  15 +
  16 +import javax.annotation.Resource;
  17 +
  18 +import java.util.Map;
  19 +
  20 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  21 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  22 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  23 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  24 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_ACCOUNT_NOT_EXISTS;
  25 +import static org.junit.jupiter.api.Assertions.*;
  26 +import static org.mockito.Mockito.verify;
  27 +
  28 +/**
  29 +* {@link MailAccountServiceImpl} 的单元测试类
  30 +*
  31 +* @author 芋道源码
  32 +*/
  33 +@Import(MailAccountServiceImpl.class)
  34 +public class MailAccountServiceImplTest extends BaseDbUnitTest {
  35 +
  36 + @Resource
  37 + private MailAccountServiceImpl mailAccountService;
  38 +
  39 + @Resource
  40 + private MailAccountMapper mailAccountMapper;
  41 +
  42 + @MockBean
  43 + private MailTemplateService mailTemplateService;
  44 + @MockBean
  45 + private MailProducer mailProducer;
  46 +
  47 + @Test
  48 + public void testInitLocalCache() {
  49 + MailAccountDO accountDO1 = randomPojo(MailAccountDO.class);
  50 + mailAccountMapper.insert(accountDO1);
  51 + MailAccountDO accountDO02 = randomPojo(MailAccountDO.class);
  52 + mailAccountMapper.insert(accountDO02);
  53 +
  54 + // 调用
  55 + mailAccountService.initLocalCache();
  56 + // 断言 mailAccountCache 缓存
  57 + Map<Long, MailAccountDO> mailAccountCache = mailAccountService.getMailAccountCache();
  58 + assertPojoEquals(accountDO1, mailAccountCache.get(accountDO1.getId()));
  59 + assertPojoEquals(accountDO02, mailAccountCache.get(accountDO02.getId()));
  60 + }
  61 +
  62 + @Test
  63 + public void testCreateMailAccount_success() {
  64 + // 准备参数
  65 + MailAccountCreateReqVO reqVO = randomPojo(MailAccountCreateReqVO.class, o -> o.setMail(randomEmail()));
  66 +
  67 + // 调用
  68 + Long mailAccountId = mailAccountService.createMailAccount(reqVO);
  69 + // 断言
  70 + assertNotNull(mailAccountId);
  71 + // 校验记录的属性是否正确
  72 + MailAccountDO mailAccount = mailAccountMapper.selectById(mailAccountId);
  73 + assertPojoEquals(reqVO, mailAccount);
  74 + verify(mailProducer).sendMailAccountRefreshMessage();
  75 + }
  76 +
  77 + @Test
  78 + public void testUpdateMailAccount_success() {
  79 + // mock 数据
  80 + MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class);
  81 + mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据
  82 + // 准备参数
  83 + MailAccountUpdateReqVO reqVO = randomPojo(MailAccountUpdateReqVO.class, o -> {
  84 + o.setId(dbMailAccount.getId()); // 设置更新的 ID
  85 + o.setMail(randomEmail());
  86 + });
  87 +
  88 + // 调用
  89 + mailAccountService.updateMailAccount(reqVO);
  90 + // 校验是否更新正确
  91 + MailAccountDO mailAccount = mailAccountMapper.selectById(reqVO.getId()); // 获取最新的
  92 + assertPojoEquals(reqVO, mailAccount);
  93 + verify(mailProducer).sendMailAccountRefreshMessage();
  94 + }
  95 +
  96 + @Test
  97 + public void testUpdateMailAccount_notExists() {
  98 + // 准备参数
  99 + MailAccountUpdateReqVO reqVO = randomPojo(MailAccountUpdateReqVO.class);
  100 +
  101 + // 调用, 并断言异常
  102 + assertServiceException(() -> mailAccountService.updateMailAccount(reqVO), MAIL_ACCOUNT_NOT_EXISTS);
  103 + }
  104 +
  105 + @Test
  106 + public void testDeleteMailAccount_success() {
  107 + // mock 数据
  108 + MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class);
  109 + mailAccountMapper.insert(dbMailAccount);// @Sql: 先插入出一条存在的数据
  110 + // 准备参数
  111 + Long id = dbMailAccount.getId();
  112 +
  113 + // 调用
  114 + mailAccountService.deleteMailAccount(id);
  115 + // 校验数据不存在了
  116 + assertNull(mailAccountMapper.selectById(id));
  117 + verify(mailProducer).sendMailAccountRefreshMessage();
  118 + }
  119 +
  120 + @Test
  121 + public void testDeleteMailAccount_notExists() {
  122 + // 准备参数
  123 + Long id = randomLongId();
  124 +
  125 + // 调用, 并断言异常
  126 + assertServiceException(() -> mailAccountService.deleteMailAccount(id), MAIL_ACCOUNT_NOT_EXISTS);
  127 + }
  128 +
  129 + @Test
  130 + public void testGetMailAccountPage() {
  131 + // mock 数据
  132 + MailAccountDO dbMailAccount = randomPojo(MailAccountDO.class, o -> { // 等会查询到
  133 + o.setMail("768@qq.com");
  134 + o.setUsername("yunai");
  135 + });
  136 + mailAccountMapper.insert(dbMailAccount);
  137 + // 测试 mail 不匹配
  138 + mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setMail("788@qq.com")));
  139 + // 测试 username 不匹配
  140 + mailAccountMapper.insert(cloneIgnoreId(dbMailAccount, o -> o.setUsername("tudou")));
  141 + // 准备参数
  142 + MailAccountPageReqVO reqVO = new MailAccountPageReqVO();
  143 + reqVO.setMail("768");
  144 + reqVO.setUsername("yu");
  145 +
  146 + // 调用
  147 + PageResult<MailAccountDO> pageResult = mailAccountService.getMailAccountPage(reqVO);
  148 + // 断言
  149 + assertEquals(1, pageResult.getTotal());
  150 + assertEquals(1, pageResult.getList().size());
  151 + assertPojoEquals(dbMailAccount, pageResult.getList().get(0));
  152 + }
  153 +
  154 +}
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java 0 → 100755
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.hutool.core.map.MapUtil;
  4 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  7 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.log.MailLogPageReqVO;
  8 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  9 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailLogDO;
  10 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  11 +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailLogMapper;
  12 +import cn.iocoder.yudao.module.system.enums.mail.MailSendStatusEnum;
  13 +import org.junit.jupiter.api.Test;
  14 +import org.springframework.context.annotation.Import;
  15 +
  16 +import javax.annotation.Resource;
  17 +import java.util.Map;
  18 +
  19 +import static cn.hutool.core.util.RandomUtil.randomEle;
  20 +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime;
  21 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
  22 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  23 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  24 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  25 +import static org.junit.jupiter.api.Assertions.*;
  26 +
  27 +/**
  28 +* {@link MailLogServiceImpl} 的单元测试类
  29 +*
  30 +* @author 芋道源码
  31 +*/
  32 +@Import(MailLogServiceImpl.class)
  33 +public class MailLogServiceImplTest extends BaseDbUnitTest {
  34 +
  35 + @Resource
  36 + private MailLogServiceImpl mailLogService;
  37 +
  38 + @Resource
  39 + private MailLogMapper mailLogMapper;
  40 +
  41 + @Test
  42 + public void testCreateMailLog() {
  43 + // 准备参数
  44 + Long userId = randomLongId();
  45 + Integer userType = randomEle(UserTypeEnum.values()).getValue();
  46 + String toMail = randomEmail();
  47 + MailAccountDO account = randomPojo(MailAccountDO.class);
  48 + MailTemplateDO template = randomPojo(MailTemplateDO.class);
  49 + String templateContent = randomString();
  50 + Map<String, Object> templateParams = randomTemplateParams();
  51 + Boolean isSend = true;
  52 + // mock 方法
  53 +
  54 + // 调用
  55 + Long logId = mailLogService.createMailLog(userId, userType, toMail, account, template, templateContent, templateParams, isSend);
  56 + // 断言
  57 + MailLogDO log = mailLogMapper.selectById(logId);
  58 + assertNotNull(log);
  59 + assertEquals(MailSendStatusEnum.INIT.getStatus(), log.getSendStatus());
  60 + assertEquals(userId, log.getUserId());
  61 + assertEquals(userType, log.getUserType());
  62 + assertEquals(toMail, log.getToMail());
  63 + assertEquals(account.getId(), log.getAccountId());
  64 + assertEquals(account.getMail(), log.getFromMail());
  65 + assertEquals(template.getId(), log.getTemplateId());
  66 + assertEquals(template.getCode(), log.getTemplateCode());
  67 + assertEquals(template.getNickname(), log.getTemplateNickname());
  68 + assertEquals(template.getTitle(), log.getTemplateTitle());
  69 + assertEquals(templateContent, log.getTemplateContent());
  70 + assertEquals(templateParams, log.getTemplateParams());
  71 + }
  72 +
  73 + @Test
  74 + public void testUpdateMailSendResult_success() {
  75 + // mock 数据
  76 + MailLogDO log = randomPojo(MailLogDO.class, o -> {
  77 + o.setSendStatus(MailSendStatusEnum.INIT.getStatus());
  78 + o.setSendTime(null).setSendMessageId(null).setSendException(null)
  79 + .setTemplateParams(randomTemplateParams());
  80 + });
  81 + mailLogMapper.insert(log);
  82 + // 准备参数
  83 + Long logId = log.getId();
  84 + String messageId = randomString();
  85 +
  86 + // 调用
  87 + mailLogService.updateMailSendResult(logId, messageId, null);
  88 + // 断言
  89 + MailLogDO dbLog = mailLogMapper.selectById(logId);
  90 + assertEquals(MailSendStatusEnum.SUCCESS.getStatus(), dbLog.getSendStatus());
  91 + assertNotNull(dbLog.getSendTime());
  92 + assertEquals(messageId, dbLog.getSendMessageId());
  93 + assertNull(dbLog.getSendException());
  94 + }
  95 +
  96 + @Test
  97 + public void testUpdateMailSendResult_exception() {
  98 + // mock 数据
  99 + MailLogDO log = randomPojo(MailLogDO.class, o -> {
  100 + o.setSendStatus(MailSendStatusEnum.INIT.getStatus());
  101 + o.setSendTime(null).setSendMessageId(null).setSendException(null)
  102 + .setTemplateParams(randomTemplateParams());
  103 + });
  104 + mailLogMapper.insert(log);
  105 + // 准备参数
  106 + Long logId = log.getId();
  107 + Exception exception = new NullPointerException("测试异常");
  108 +
  109 + // 调用
  110 + mailLogService.updateMailSendResult(logId, null, exception);
  111 + // 断言
  112 + MailLogDO dbLog = mailLogMapper.selectById(logId);
  113 + assertEquals(MailSendStatusEnum.FAILURE.getStatus(), dbLog.getSendStatus());
  114 + assertNotNull(dbLog.getSendTime());
  115 + assertNull(dbLog.getSendMessageId());
  116 + assertEquals("NullPointerException: 测试异常", dbLog.getSendException());
  117 + }
  118 +
  119 + @Test
  120 + public void testGetMailLogPage() {
  121 + // mock 数据
  122 + MailLogDO dbMailLog = randomPojo(MailLogDO.class, o -> { // 等会查询到
  123 + o.setUserId(1L);
  124 + o.setUserType(UserTypeEnum.ADMIN.getValue());
  125 + o.setToMail("768@qq.com");
  126 + o.setAccountId(10L);
  127 + o.setTemplateId(100L);
  128 + o.setSendStatus(MailSendStatusEnum.INIT.getStatus());
  129 + o.setSendTime(buildTime(2023, 2, 10));
  130 + o.setTemplateParams(randomTemplateParams());
  131 + });
  132 + mailLogMapper.insert(dbMailLog);
  133 + // 测试 userId 不匹配
  134 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserId(2L)));
  135 + // 测试 userType 不匹配
  136 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
  137 + // 测试 toMail 不匹配
  138 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setToMail("788@.qq.com")));
  139 + // 测试 accountId 不匹配
  140 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setAccountId(11L)));
  141 + // 测试 templateId 不匹配
  142 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setTemplateId(101L)));
  143 + // 测试 sendStatus 不匹配
  144 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setSendStatus(MailSendStatusEnum.SUCCESS.getStatus())));
  145 + // 测试 sendTime 不匹配
  146 + mailLogMapper.insert(cloneIgnoreId(dbMailLog, o -> o.setSendTime(buildTime(2023, 3, 10))));
  147 + // 准备参数
  148 + MailLogPageReqVO reqVO = new MailLogPageReqVO();
  149 + reqVO.setUserId(1L);
  150 + reqVO.setUserType(UserTypeEnum.ADMIN.getValue());
  151 + reqVO.setToMail("768");
  152 + reqVO.setAccountId(10L);
  153 + reqVO.setTemplateId(100L);
  154 + reqVO.setSendStatus(MailSendStatusEnum.INIT.getStatus());
  155 + reqVO.setSendTime((buildBetweenTime(2023, 2, 1, 2023, 2, 15)));
  156 +
  157 + // 调用
  158 + PageResult<MailLogDO> pageResult = mailLogService.getMailLogPage(reqVO);
  159 + // 断言
  160 + assertEquals(1, pageResult.getTotal());
  161 + assertEquals(1, pageResult.getList().size());
  162 + assertPojoEquals(dbMailLog, pageResult.getList().get(0));
  163 + }
  164 +
  165 + private static Map<String, Object> randomTemplateParams() {
  166 + return MapUtil.<String, Object>builder().put(randomString(), randomString())
  167 + .put(randomString(), randomString()).build();
  168 + }
  169 +}
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailSendServiceImplTest.java 0 → 100644
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.hutool.core.map.MapUtil;
  4 +import cn.hutool.extra.mail.MailAccount;
  5 +import cn.hutool.extra.mail.MailUtil;
  6 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  7 +import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
  8 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
  9 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  10 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  11 +import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
  12 +import org.assertj.core.util.Lists;
  13 +import org.junit.jupiter.api.Disabled;
  14 +import org.junit.jupiter.api.Test;
  15 +import org.mockito.InjectMocks;
  16 +import org.mockito.Mock;
  17 +
  18 +import java.util.HashMap;
  19 +import java.util.Map;
  20 +
  21 +import static cn.hutool.core.util.RandomUtil.randomEle;
  22 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  23 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  24 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
  25 +import static org.junit.jupiter.api.Assertions.assertEquals;
  26 +import static org.mockito.ArgumentMatchers.eq;
  27 +import static org.mockito.Mockito.*;
  28 +
  29 +class MailSendServiceImplTest extends BaseMockitoUnitTest {
  30 +
  31 + @InjectMocks
  32 + private MailSendServiceImpl mailSendService;
  33 +
  34 + @Mock
  35 + private MailAccountService mailAccountService;
  36 + @Mock
  37 + private MailTemplateService mailTemplateService;
  38 + @Mock
  39 + private MailLogService mailLogService;
  40 + @Mock
  41 + private MailProducer mailProducer;
  42 +
  43 + /**
  44 + * 用于快速测试你的邮箱账号是否正常
  45 + */
  46 + @Test
  47 + @Disabled
  48 + public void testDemo() {
  49 + MailAccount mailAccount = new MailAccount()
  50 +// .setFrom("奥特曼 <ydym_test@163.com>")
  51 + .setFrom("ydym_test@163.com") // 邮箱地址
  52 + .setHost("smtp.163.com").setPort(465).setSslEnable(true) // SMTP 服务器
  53 + .setAuth(true).setUser("ydym_test@163.com").setPass("WBZTEINMIFVRYSOE"); // 登录账号密码
  54 + String messageId = MailUtil.send(mailAccount, "7685413@qq.com", "主题", "内容", false);
  55 + System.out.println("发送结果:" + messageId);
  56 + }
  57 +
  58 + /**
  59 + * 发送成功,当短信模板开启时
  60 + */
  61 + @Test
  62 + public void testSendSingleMail_successWhenMailTemplateEnable() {
  63 + // 准备参数
  64 + String mail = randomEmail();
  65 + Long userId = randomLongId();
  66 + Integer userType = randomEle(UserTypeEnum.values()).getValue();
  67 + String templateCode = randomString();
  68 + Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
  69 + .put("op", "login").build();
  70 + // mock MailTemplateService 的方法
  71 + MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> {
  72 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  73 + o.setContent("验证码为{code}, 操作为{op}");
  74 + o.setParams(Lists.newArrayList("code", "op"));
  75 + });
  76 + when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
  77 + String content = randomString();
  78 + when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams)))
  79 + .thenReturn(content);
  80 + // mock MailAccountService 的方法
  81 + MailAccountDO account = randomPojo(MailAccountDO.class);
  82 + when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account);
  83 + // mock MailLogService 的方法
  84 + Long mailLogId = randomLongId();
  85 + when(mailLogService.createMailLog(eq(userId), eq(userType), eq(mail),
  86 + eq(account), eq(template), eq(content), eq(templateParams), eq(true))).thenReturn(mailLogId);
  87 +
  88 + // 调用
  89 + Long resultMailLogId = mailSendService.sendSingleMail(mail, userId, userType, templateCode, templateParams);
  90 + // 断言
  91 + assertEquals(mailLogId, resultMailLogId);
  92 + // 断言调用
  93 + verify(mailProducer).sendMailSendMessage(eq(mailLogId), eq(mail),
  94 + eq(account.getId()), eq(template.getNickname()), eq(template.getTitle()), eq(content));
  95 + }
  96 +
  97 + /**
  98 + * 发送成功,当短信模板关闭时
  99 + */
  100 + @Test
  101 + public void testSendSingleMail_successWhenSmsTemplateDisable() {
  102 + // 准备参数
  103 + String mail = randomEmail();
  104 + Long userId = randomLongId();
  105 + Integer userType = randomEle(UserTypeEnum.values()).getValue();
  106 + String templateCode = randomString();
  107 + Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
  108 + .put("op", "login").build();
  109 + // mock MailTemplateService 的方法
  110 + MailTemplateDO template = randomPojo(MailTemplateDO.class, o -> {
  111 + o.setStatus(CommonStatusEnum.DISABLE.getStatus());
  112 + o.setContent("验证码为{code}, 操作为{op}");
  113 + o.setParams(Lists.newArrayList("code", "op"));
  114 + });
  115 + when(mailTemplateService.getMailTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
  116 + String content = randomString();
  117 + when(mailTemplateService.formatMailTemplateContent(eq(template.getContent()), eq(templateParams)))
  118 + .thenReturn(content);
  119 + // mock MailAccountService 的方法
  120 + MailAccountDO account = randomPojo(MailAccountDO.class);
  121 + when(mailAccountService.getMailAccountFromCache(eq(template.getAccountId()))).thenReturn(account);
  122 + // mock MailLogService 的方法
  123 + Long mailLogId = randomLongId();
  124 + when(mailLogService.createMailLog(eq(userId), eq(userType), eq(mail),
  125 + eq(account), eq(template), eq(content), eq(templateParams), eq(false))).thenReturn(mailLogId);
  126 +
  127 + // 调用
  128 + Long resultMailLogId = mailSendService.sendSingleMail(mail, userId, userType, templateCode, templateParams);
  129 + // 断言
  130 + assertEquals(mailLogId, resultMailLogId);
  131 + // 断言调用
  132 + verify(mailProducer, times(0)).sendMailSendMessage(anyLong(), anyString(),
  133 + anyLong(), anyString(), anyString(), anyString());
  134 + }
  135 +
  136 + @Test
  137 + public void testCheckMailTemplateValid_notExists() {
  138 + // 准备参数
  139 + String templateCode = randomString();
  140 + // mock 方法
  141 +
  142 + // 调用,并断言异常
  143 + assertServiceException(() -> mailSendService.checkMailTemplateValid(templateCode),
  144 + MAIL_TEMPLATE_NOT_EXISTS);
  145 + }
  146 +
  147 + @Test
  148 + public void testBuildTemplateParams_paramMiss() {
  149 + // 准备参数
  150 + MailTemplateDO template = randomPojo(MailTemplateDO.class,
  151 + o -> o.setParams(Lists.newArrayList("code")));
  152 + Map<String, Object> templateParams = new HashMap<>();
  153 + // mock 方法
  154 +
  155 + // 调用,并断言异常
  156 + assertServiceException(() -> mailSendService.buildTemplateParams(template, templateParams),
  157 + MAIL_SEND_TEMPLATE_PARAM_MISS, "code");
  158 + }
  159 +
  160 + @Test
  161 + public void testCheckMail_notExists() {
  162 + // 准备参数
  163 + // mock 方法
  164 +
  165 + // 调用,并断言异常
  166 + assertServiceException(() -> mailSendService.checkMail(null),
  167 + MAIL_SEND_MAIL_NOT_EXISTS);
  168 + }
  169 +
  170 +}
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java 0 → 100755
  1 +package cn.iocoder.yudao.module.system.service.mail;
  2 +
  3 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  6 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateCreateReqVO;
  7 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplatePageReqVO;
  8 +import cn.iocoder.yudao.module.system.controller.admin.mail.vo.template.MailTemplateUpdateReqVO;
  9 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
  10 +import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
  11 +import cn.iocoder.yudao.module.system.dal.mysql.mail.MailTemplateMapper;
  12 +import cn.iocoder.yudao.module.system.mq.producer.mail.MailProducer;
  13 +import org.junit.jupiter.api.Test;
  14 +import org.springframework.boot.test.mock.mockito.MockBean;
  15 +import org.springframework.context.annotation.Import;
  16 +
  17 +import javax.annotation.Resource;
  18 +
  19 +import java.util.Map;
  20 +
  21 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
  22 +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
  23 +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  24 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  25 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
  26 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
  27 +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
  28 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.MAIL_TEMPLATE_NOT_EXISTS;
  29 +import static org.junit.jupiter.api.Assertions.*;
  30 +
  31 +/**
  32 +* {@link MailTemplateServiceImpl} 的单元测试类
  33 +*
  34 +* @author 芋道源码
  35 +*/
  36 +@Import(MailTemplateServiceImpl.class)
  37 +public class MailTemplateServiceImplTest extends BaseDbUnitTest {
  38 +
  39 + @Resource
  40 + private MailTemplateServiceImpl mailTemplateService;
  41 +
  42 + @Resource
  43 + private MailTemplateMapper mailTemplateMapper;
  44 +
  45 + @MockBean
  46 + private MailProducer mailProducer;
  47 +
  48 + @Test
  49 + public void testInitLocalCache() {
  50 + MailTemplateDO templateDO01 = randomPojo(MailTemplateDO.class);
  51 + mailTemplateMapper.insert(templateDO01);
  52 + MailTemplateDO templateDO02 = randomPojo(MailTemplateDO.class);
  53 + mailTemplateMapper.insert(templateDO02);
  54 +
  55 + // 调用
  56 + mailTemplateService.initLocalCache();
  57 + // 断言 mailTemplateCache 缓存
  58 + Map<String, MailTemplateDO> mailTemplateCache = mailTemplateService.getMailTemplateCache();
  59 + assertPojoEquals(templateDO01, mailTemplateCache.get(templateDO01.getCode()));
  60 + assertPojoEquals(templateDO02, mailTemplateCache.get(templateDO02.getCode()));
  61 + }
  62 +
  63 + @Test
  64 + public void testCreateMailTemplate_success() {
  65 + // 准备参数
  66 + MailTemplateCreateReqVO reqVO = randomPojo(MailTemplateCreateReqVO.class);
  67 +
  68 + // 调用
  69 + Long mailTemplateId = mailTemplateService.createMailTemplate(reqVO);
  70 + // 断言
  71 + assertNotNull(mailTemplateId);
  72 + // 校验记录的属性是否正确
  73 + MailTemplateDO mailTemplate = mailTemplateMapper.selectById(mailTemplateId);
  74 + assertPojoEquals(reqVO, mailTemplate);
  75 + }
  76 +
  77 + @Test
  78 + public void testUpdateMailTemplate_success() {
  79 + // mock 数据
  80 + MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class);
  81 + mailTemplateMapper.insert(dbMailTemplate);// @Sql: 先插入出一条存在的数据
  82 + // 准备参数
  83 + MailTemplateUpdateReqVO reqVO = randomPojo(MailTemplateUpdateReqVO.class, o -> {
  84 + o.setId(dbMailTemplate.getId()); // 设置更新的 ID
  85 + });
  86 +
  87 + // 调用
  88 + mailTemplateService.updateMailTemplate(reqVO);
  89 + // 校验是否更新正确
  90 + MailTemplateDO mailTemplate = mailTemplateMapper.selectById(reqVO.getId()); // 获取最新的
  91 + assertPojoEquals(reqVO, mailTemplate);
  92 + }
  93 +
  94 + @Test
  95 + public void testUpdateMailTemplate_notExists() {
  96 + // 准备参数
  97 + MailTemplateUpdateReqVO reqVO = randomPojo(MailTemplateUpdateReqVO.class);
  98 +
  99 + // 调用, 并断言异常
  100 + assertServiceException(() -> mailTemplateService.updateMailTemplate(reqVO), MAIL_TEMPLATE_NOT_EXISTS);
  101 + }
  102 +
  103 + @Test
  104 + public void testDeleteMailTemplate_success() {
  105 + // mock 数据
  106 + MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class);
  107 + mailTemplateMapper.insert(dbMailTemplate);// @Sql: 先插入出一条存在的数据
  108 + // 准备参数
  109 + Long id = dbMailTemplate.getId();
  110 +
  111 + // 调用
  112 + mailTemplateService.deleteMailTemplate(id);
  113 + // 校验数据不存在了
  114 + assertNull(mailTemplateMapper.selectById(id));
  115 + }
  116 +
  117 + @Test
  118 + public void testDeleteMailTemplate_notExists() {
  119 + // 准备参数
  120 + Long id = randomLongId();
  121 +
  122 + // 调用, 并断言异常
  123 + assertServiceException(() -> mailTemplateService.deleteMailTemplate(id), MAIL_TEMPLATE_NOT_EXISTS);
  124 + }
  125 +
  126 + @Test
  127 + public void testGetMailTemplatePage() {
  128 + // mock 数据
  129 + MailTemplateDO dbMailTemplate = randomPojo(MailTemplateDO.class, o -> { // 等会查询到
  130 + o.setName("源码");
  131 + o.setCode("test_01");
  132 + o.setAccountId(1L);
  133 + o.setStatus(CommonStatusEnum.ENABLE.getStatus());
  134 + o.setCreateTime(buildTime(2023, 2, 3));
  135 + });
  136 + mailTemplateMapper.insert(dbMailTemplate);
  137 + // 测试 name 不匹配
  138 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setName("芋道")));
  139 + // 测试 code 不匹配
  140 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCode("test_02")));
  141 + // 测试 accountId 不匹配
  142 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setAccountId(2L)));
  143 + // 测试 status 不匹配
  144 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
  145 + // 测试 createTime 不匹配
  146 + mailTemplateMapper.insert(cloneIgnoreId(dbMailTemplate, o -> o.setCreateTime(buildTime(2023, 1, 5))));
  147 + // 准备参数
  148 + MailTemplatePageReqVO reqVO = new MailTemplatePageReqVO();
  149 + reqVO.setName("源");
  150 + reqVO.setCode("est_01");
  151 + reqVO.setAccountId(1L);
  152 + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
  153 + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 5));
  154 +
  155 + // 调用
  156 + PageResult<MailTemplateDO> pageResult = mailTemplateService.getMailTemplatePage(reqVO);
  157 + // 断言
  158 + assertEquals(1, pageResult.getTotal());
  159 + assertEquals(1, pageResult.getList().size());
  160 + assertPojoEquals(dbMailTemplate, pageResult.getList().get(0));
  161 + }
  162 +
  163 +}
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/oauth2/OAuth2ClientServiceImplTest.java
... ... @@ -19,7 +19,6 @@ import java.util.Collections;
19 19 import java.util.Map;
20 20  
21 21 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
22   -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
23 22 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
24 23 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
25 24 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
... ... @@ -59,8 +58,6 @@ public class OAuth2ClientServiceImplTest extends BaseDbUnitTest {
59 58 assertEquals(2, clientCache.size());
60 59 assertPojoEquals(clientDO1, clientCache.get(clientDO1.getClientId()));
61 60 assertPojoEquals(clientDO2, clientCache.get(clientDO2.getClientId()));
62   - // 断言 maxUpdateTime 缓存
63   - assertEquals(max(clientDO1.getUpdateTime(), clientDO2.getUpdateTime()), oauth2ClientService.getMaxUpdateTime());
64 61 }
65 62  
66 63 @Test
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/MenuServiceTest.java
... ... @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.system.service.permission;
3 3 import cn.hutool.core.bean.BeanUtil;
4 4 import cn.hutool.core.lang.Assert;
5 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6   -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
7 6 import cn.iocoder.yudao.framework.common.util.spring.SpringAopUtils;
8 7 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
9 8 import cn.iocoder.yudao.module.system.controller.admin.permission.vo.menu.MenuCreateReqVO;
... ... @@ -20,7 +19,6 @@ import org.springframework.boot.test.mock.mockito.MockBean;
20 19 import org.springframework.context.annotation.Import;
21 20  
22 21 import javax.annotation.Resource;
23   -import java.time.LocalDateTime;
24 22 import java.util.*;
25 23  
26 24 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
... ... @@ -51,32 +49,24 @@ public class MenuServiceTest extends BaseDbUnitTest {
51 49 private TenantService tenantService;
52 50  
53 51 @Test
54   - public void testInitLocalCache_success() throws Exception {
55   - MenuDO menuDO1 = createMenuDO(MenuTypeEnum.MENU, "xxxx", 0L);
  52 + public void testInitLocalCache_success() {
  53 + MenuDO menuDO1 = randomPojo(MenuDO.class);
56 54 menuMapper.insert(menuDO1);
57   - MenuDO menuDO2 = createMenuDO(MenuTypeEnum.MENU, "xxxx", 0L);
  55 + MenuDO menuDO2 = randomPojo(MenuDO.class);
58 56 menuMapper.insert(menuDO2);
59 57  
60 58 // 调用
61 59 menuService.initLocalCache();
62   -
63   - // 获取代理对象
64   - MenuServiceImpl target = (MenuServiceImpl) SpringAopUtils.getTarget(menuService);
65   -
66   - Map<Long, MenuDO> menuCache =
67   - (Map<Long, MenuDO>) BeanUtil.getFieldValue(target, "menuCache");
  60 + // 校验 menuCache 缓存
  61 + Map<Long, MenuDO> menuCache = menuService.getMenuCache();
68 62 Assert.isTrue(menuCache.size() == 2);
69 63 assertPojoEquals(menuDO1, menuCache.get(menuDO1.getId()));
70 64 assertPojoEquals(menuDO2, menuCache.get(menuDO2.getId()));
71   -
72   - Multimap<String, MenuDO> permissionMenuCache =
73   - (Multimap<String, MenuDO>) BeanUtil.getFieldValue(target, "permissionMenuCache");
  65 + // 校验 permissionMenuCache 缓存
  66 + Multimap<String, MenuDO> permissionMenuCache = menuService.getPermissionMenuCache();
74 67 Assert.isTrue(permissionMenuCache.size() == 2);
75 68 assertPojoEquals(menuDO1, permissionMenuCache.get(menuDO1.getPermission()));
76 69 assertPojoEquals(menuDO2, permissionMenuCache.get(menuDO2.getPermission()));
77   -
78   - LocalDateTime maxUpdateTime = (LocalDateTime) BeanUtil.getFieldValue(target, "maxUpdateTime");
79   - assertEquals(ObjectUtils.max(menuDO1.getUpdateTime(), menuDO2.getUpdateTime()), maxUpdateTime);
80 70 }
81 71  
82 72 @Test
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java
... ... @@ -3,18 +3,15 @@ package cn.iocoder.yudao.module.system.service.permission;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.map.MapUtil;
5 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6   -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
7   -import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
8 6 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  7 +import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
9 8 import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
10 9 import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
11 10 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
12 11 import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleMenuDO;
13 12 import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO;
14 13 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
15   -import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuBatchInsertMapper;
16 14 import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper;
17   -import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleBatchInsertMapper;
18 15 import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper;
19 16 import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum;
20 17 import cn.iocoder.yudao.module.system.mq.producer.permission.PermissionProducer;
... ... @@ -27,8 +24,10 @@ import org.springframework.boot.test.mock.mockito.MockBean;
27 24 import org.springframework.context.annotation.Import;
28 25  
29 26 import javax.annotation.Resource;
30   -import java.time.LocalDateTime;
31   -import java.util.*;
  27 +import java.util.Collection;
  28 +import java.util.List;
  29 +import java.util.Map;
  30 +import java.util.Set;
32 31  
33 32 import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
34 33 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
... ... @@ -42,8 +41,7 @@ import static org.mockito.ArgumentMatchers.same;
42 41 import static org.mockito.Mockito.verify;
43 42 import static org.mockito.Mockito.when;
44 43  
45   -@Import({PermissionServiceImpl.class,
46   - RoleMenuBatchInsertMapper.class, UserRoleBatchInsertMapper.class})
  44 +@Import({PermissionServiceImpl.class})
47 45 public class PermissionServiceTest extends BaseDbUnitTest {
48 46  
49 47 @Resource
... ... @@ -52,11 +50,7 @@ public class PermissionServiceTest extends BaseDbUnitTest {
52 50 @Resource
53 51 private RoleMenuMapper roleMenuMapper;
54 52 @Resource
55   - private RoleMenuBatchInsertMapper roleMenuBatchInsertMapper;
56   - @Resource
57 53 private UserRoleMapper userRoleMapper;
58   - @Resource
59   - private UserRoleBatchInsertMapper userRoleBatchInsertMapper;
60 54  
61 55 @MockBean
62 56 private RoleService roleService;
... ... @@ -71,7 +65,7 @@ public class PermissionServiceTest extends BaseDbUnitTest {
71 65 private PermissionProducer permissionProducer;
72 66  
73 67 @Test
74   - public void testInitLocalCacheIfUpdateForRoleMenu() {
  68 + public void testInitLocalCacheForRoleMenu() {
75 69 // mock 数据
76 70 RoleMenuDO roleMenuDO01 = randomPojo(RoleMenuDO.class, o -> o.setRoleId(1L).setMenuId(10L));
77 71 roleMenuMapper.insert(roleMenuDO01);
... ... @@ -79,7 +73,7 @@ public class PermissionServiceTest extends BaseDbUnitTest {
79 73 roleMenuMapper.insert(roleMenuDO02);
80 74  
81 75 // 调用
82   - permissionService.initLocalCacheIfUpdateForRoleMenu(null);
  76 + permissionService.initLocalCacheForRoleMenu();
83 77 // 断言 roleMenuCache 缓存
84 78 assertEquals(1, permissionService.getRoleMenuCache().keySet().size());
85 79 assertEquals(asList(10L, 20L), permissionService.getRoleMenuCache().get(1L));
... ... @@ -87,13 +81,10 @@ public class PermissionServiceTest extends BaseDbUnitTest {
87 81 assertEquals(2, permissionService.getMenuRoleCache().size());
88 82 assertEquals(singletonList(1L), permissionService.getMenuRoleCache().get(10L));
89 83 assertEquals(singletonList(1L), permissionService.getMenuRoleCache().get(20L));
90   - // 断言 maxUpdateTime 缓存
91   - LocalDateTime maxUpdateTime = permissionService.getRoleMenuMaxUpdateTime();
92   - assertEquals(ObjectUtils.max(roleMenuDO01.getUpdateTime(), roleMenuDO02.getUpdateTime()), maxUpdateTime);
93 84 }
94 85  
95 86 @Test
96   - public void testInitLocalCacheIfUpdateForUserRole() {
  87 + public void testInitLocalCacheForUserRole() {
97 88 // mock 数据
98 89 UserRoleDO userRoleDO01 = randomPojo(UserRoleDO.class, o -> o.setUserId(1L).setRoleId(10L));
99 90 userRoleMapper.insert(userRoleDO01);
... ... @@ -101,13 +92,10 @@ public class PermissionServiceTest extends BaseDbUnitTest {
101 92 userRoleMapper.insert(roleMenuDO02);
102 93  
103 94 // 调用
104   - permissionService.initLocalCacheIfUpdateForUserRole(null);
  95 + permissionService.initLocalCacheForUserRole();
105 96 // 断言 roleMenuCache 缓存
106 97 assertEquals(1, permissionService.getUserRoleCache().size());
107 98 assertEquals(asSet(10L, 20L), permissionService.getUserRoleCache().get(1L));
108   - // 断言 maxUpdateTime 缓存
109   - LocalDateTime maxUpdateTime = permissionService.getUserRoleMaxUpdateTime();
110   - assertEquals(ObjectUtils.max(userRoleDO01.getUpdateTime(), roleMenuDO02.getUpdateTime()), maxUpdateTime);
111 99 }
112 100  
113 101 @Test
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceTest.java
... ... @@ -23,7 +23,6 @@ import java.util.*;
23 23  
24 24 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
25 25 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
26   -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
27 26 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
28 27 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
29 28 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
... ... @@ -59,8 +58,6 @@ public class RoleServiceTest extends BaseDbUnitTest {
59 58 Map<Long, RoleDO> roleCache = roleService.getRoleCache();
60 59 assertPojoEquals(roleDO1, roleCache.get(roleDO1.getId()));
61 60 assertPojoEquals(roleDO2, roleCache.get(roleDO2.getId()));
62   - // 断言 maxUpdateTime 缓存
63   - assertEquals(max(roleDO1.getUpdateTime(), roleDO2.getUpdateTime()), roleService.getMaxUpdateTime());
64 61 }
65 62  
66 63 @Test
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImplTest.java
... ... @@ -60,8 +60,6 @@ public class SensitiveWordServiceImplTest extends BaseDbUnitTest {
60 60  
61 61 // 调用
62 62 sensitiveWordService.initLocalCache();
63   - // 断言 maxUpdateTime 缓存
64   - assertEquals(max(wordDO1.getUpdateTime(), wordDO2.getUpdateTime()), sensitiveWordService.getMaxUpdateTime());
65 63 // 断言 sensitiveWordTagsCache 缓存
66 64 assertEquals(SetUtils.asSet("论坛", "蔬菜"), sensitiveWordService.getSensitiveWordTags());
67 65 // 断言 tagSensitiveWordTries 缓存
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java
1 1 package cn.iocoder.yudao.module.system.service.sms;
2 2  
3   -import cn.hutool.core.bean.BeanUtil;
4   -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
5   -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
6   -import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
7   -import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
8   -import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
9   -import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
10 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
11 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
12 5 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
13 6 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
14 7 import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
15 8 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
  9 +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
  10 +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
  11 +import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
  12 +import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
  13 +import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
  14 +import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
16 15 import org.junit.jupiter.api.Test;
17 16 import org.springframework.boot.test.mock.mockito.MockBean;
18 17 import org.springframework.context.annotation.Import;
... ... @@ -22,12 +21,11 @@ import java.time.LocalDateTime;
22 21 import java.util.function.Consumer;
23 22  
24 23 import static cn.hutool.core.util.RandomUtil.randomEle;
25   -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
26   -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
27 24 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
28   -import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.max;
29 25 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
30 26 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
  27 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
  28 +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
31 29 import static org.junit.jupiter.api.Assertions.*;
32 30 import static org.mockito.ArgumentMatchers.eq;
33 31 import static org.mockito.Mockito.*;
... ... @@ -58,9 +56,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
58 56  
59 57 // 调用
60 58 smsChannelService.initLocalCache();
61   - // 校验 maxUpdateTime 属性
62   - LocalDateTime maxUpdateTime = (LocalDateTime) BeanUtil.getFieldValue(smsChannelService, "maxUpdateTime");
63   - assertEquals(max(smsChannelDO01.getUpdateTime(), smsChannelDO02.getUpdateTime()), maxUpdateTime);
64 59 // 校验调用
65 60 verify(smsClientFactory, times(1)).createOrUpdateSmsClient(
66 61 argThat(properties -> isPojoEquals(smsChannelDO01, properties)));
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceTest.java
... ... @@ -235,7 +235,6 @@ public class SmsLogServiceTest extends BaseDbUnitTest {
235 235 return randomPojo(SmsLogDO.class, ArrayUtils.append(consumer, consumers));
236 236 }
237 237  
238   -
239 238 private static Map<String, Object> randomTemplateParams() {
240 239 return MapUtil.<String, Object>builder().put(randomString(), randomString())
241 240 .put(randomString(), randomString()).build();
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceTest.java
... ... @@ -61,7 +61,6 @@ public class SmsTemplateServiceTest extends BaseDbUnitTest {
61 61 private SmsProducer smsProducer;
62 62  
63 63 @Test
64   - @SuppressWarnings("unchecked")
65 64 void testInitLocalCache() {
66 65 // mock 数据
67 66 SmsTemplateDO smsTemplate01 = randomSmsTemplateDO();
... ... @@ -72,13 +71,10 @@ public class SmsTemplateServiceTest extends BaseDbUnitTest {
72 71 // 调用
73 72 smsTemplateService.initLocalCache();
74 73 // 断言 deptCache 缓存
75   - Map<String, SmsTemplateDO> smsTemplateCache = (Map<String, SmsTemplateDO>) getFieldValue(smsTemplateService, "smsTemplateCache");
  74 + Map<String, SmsTemplateDO> smsTemplateCache = smsTemplateService.getSmsTemplateCache();
76 75 assertEquals(2, smsTemplateCache.size());
77 76 assertPojoEquals(smsTemplate01, smsTemplateCache.get(smsTemplate01.getCode()));
78 77 assertPojoEquals(smsTemplate02, smsTemplateCache.get(smsTemplate02.getCode()));
79   - // 断言 maxUpdateTime 缓存
80   - LocalDateTime maxUpdateTime = (LocalDateTime) getFieldValue(smsTemplateService, "maxUpdateTime");
81   - assertEquals(max(smsTemplate01.getUpdateTime(), smsTemplate02.getUpdateTime()), maxUpdateTime);
82 78 }
83 79  
84 80 @Test
... ...
yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql
... ... @@ -25,3 +25,6 @@ DELETE FROM &quot;system_oauth2_approve&quot;;
25 25 DELETE FROM "system_oauth2_access_token";
26 26 DELETE FROM "system_oauth2_refresh_token";
27 27 DELETE FROM "system_oauth2_code";
  28 +DELETE FROM "system_mail_account";
  29 +DELETE FROM "system_mail_template";
  30 +DELETE FROM "system_mail_log";
... ...
yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql
... ... @@ -566,3 +566,63 @@ CREATE TABLE IF NOT EXISTS &quot;system_oauth2_code&quot; (
566 566 "deleted" bit NOT NULL DEFAULT FALSE,
567 567 PRIMARY KEY ("id")
568 568 ) COMMENT 'OAuth2 刷新令牌';
  569 +
  570 +CREATE TABLE IF NOT EXISTS "system_mail_account" (
  571 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  572 + "mail" varchar NOT NULL,
  573 + "username" varchar NOT NULL,
  574 + "password" varchar NOT NULL,
  575 + "host" varchar NOT NULL,
  576 + "port" int NOT NULL,
  577 + "ssl_enable" bit NOT NULL,
  578 + "creator" varchar DEFAULT '',
  579 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  580 + "updater" varchar DEFAULT '',
  581 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  582 + "deleted" bit NOT NULL DEFAULT FALSE,
  583 + PRIMARY KEY ("id")
  584 +) COMMENT '邮箱账号表';
  585 +
  586 +CREATE TABLE IF NOT EXISTS "system_mail_template" (
  587 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  588 + "name" varchar NOT NULL,
  589 + "code" varchar NOT NULL,
  590 + "account_id" bigint NOT NULL,
  591 + "nickname" varchar,
  592 + "title" varchar NOT NULL,
  593 + "content" varchar NOT NULL,
  594 + "params" varchar NOT NULL,
  595 + "status" varchar NOT NULL,
  596 + "remark" varchar,
  597 + "creator" varchar DEFAULT '',
  598 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  599 + "updater" varchar DEFAULT '',
  600 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  601 + "deleted" bit NOT NULL DEFAULT FALSE,
  602 + PRIMARY KEY ("id")
  603 +) COMMENT '邮件模版表';
  604 +
  605 +CREATE TABLE IF NOT EXISTS "system_mail_log" (
  606 + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
  607 + "user_id" bigint,
  608 + "user_type" varchar,
  609 + "to_mail" varchar NOT NULL,
  610 + "account_id" bigint NOT NULL,
  611 + "from_mail" varchar NOT NULL,
  612 + "template_id" bigint NOT NULL,
  613 + "template_code" varchar NOT NULL,
  614 + "template_nickname" varchar,
  615 + "template_title" varchar NOT NULL,
  616 + "template_content" varchar NOT NULL,
  617 + "template_params" varchar NOT NULL,
  618 + "send_status" varchar NOT NULL,
  619 + "send_time" datetime,
  620 + "send_message_id" varchar,
  621 + "send_exception" varchar,
  622 + "creator" varchar DEFAULT '',
  623 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  624 + "updater" varchar DEFAULT '',
  625 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  626 + "deleted" bit NOT NULL DEFAULT FALSE,
  627 + PRIMARY KEY ("id")
  628 +) COMMENT '邮件日志表';
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -156,6 +156,9 @@ yudao:
156 156 - system_sms_log
157 157 - system_sensitive_word
158 158 - system_oauth2_client
  159 + - system_mail_account
  160 + - system_mail_template
  161 + - system_mail_log
159 162 - infra_codegen_column
160 163 - infra_codegen_table
161 164 - infra_test_demo
... ...
yudao-ui-admin-vue3/.env.base
1 1 # 本地开发环境
2 2 NODE_ENV=development
3 3  
  4 +VITE_DEV=true
  5 +
4 6 # 请求路径
5 7 VITE_BASE_URL='http://localhost:48080'
6 8  
... ...
yudao-ui-admin-vue3/.env.dev
1 1 # 开发环境
2 2 NODE_ENV=production
3 3  
  4 +VITE_DEV=false
  5 +
4 6 # 请求路径
5 7 VITE_BASE_URL='http://localhost:58080'
6 8  
... ...
yudao-ui-admin-vue3/.env.pro
1 1 # 生产环境
2 2 NODE_ENV=production
3 3  
  4 +VITE_DEV=false
  5 +
4 6 # 请求路径
5 7 VITE_BASE_URL='http://localhost:58080'
6 8  
... ...
yudao-ui-admin-vue3/.env.test
1 1 # 测试环境
2 2 NODE_ENV=production
3 3  
  4 +VITE_DEV=false
  5 +
4 6 # 请求路径
5 7 VITE_BASE_URL='http://localhost:58080'
6 8  
... ...
yudao-ui-admin-vue3/src/api/system/mail/account/index.ts 0 → 100644
  1 +import request from '@/config/axios'
  2 +
  3 +export interface MailAccountVO {
  4 + id: number
  5 + mail: string
  6 + username: string
  7 + password: string
  8 + host: string
  9 + port: number
  10 + sslEnable: boolean
  11 +}
  12 +
  13 +export interface MailAccountPageReqVO extends PageParam {
  14 + mail?: string
  15 + username?: string
  16 +}
  17 +
  18 +// 查询邮箱账号列表
  19 +export const getMailAccountPageApi = async (params: MailAccountPageReqVO) => {
  20 + return await request.get({ url: '/system/mail-account/page', params })
  21 +}
  22 +
  23 +// 查询邮箱账号详情
  24 +export const getMailAccountApi = async (id: number) => {
  25 + return await request.get({ url: '/system/mail-account/get?id=' + id })
  26 +}
  27 +
  28 +// 新增邮箱账号
  29 +export const createMailAccountApi = async (data: MailAccountVO) => {
  30 + return await request.post({ url: '/system/mail-account/create', data })
  31 +}
  32 +
  33 +// 修改邮箱账号
  34 +export const updateMailAccountApi = async (data: MailAccountVO) => {
  35 + return await request.put({ url: '/system/mail-account/update', data })
  36 +}
  37 +
  38 +// 删除邮箱账号
  39 +export const deleteMailAccountApi = async (id: number) => {
  40 + return await request.delete({ url: '/system/mail-account/delete?id=' + id })
  41 +}
  42 +
  43 +// 获得邮箱账号精简列表
  44 +export const getSimpleMailAccounts = async () => {
  45 + return request.get({ url: '/system/mail-account/list-all-simple' })
  46 +}
... ...
yudao-ui-admin-vue3/src/api/system/mail/log/index.ts 0 → 100644
  1 +import request from '@/config/axios'
  2 +
  3 +export interface MailLogVO {
  4 + id: number
  5 + userId: number
  6 + userType: number
  7 + toMail: string
  8 + accountId: number
  9 + fromMail: string
  10 + templateId: number
  11 + templateCode: string
  12 + templateNickname: string
  13 + templateTitle: string
  14 + templateContent: string
  15 + templateParams: string
  16 + sendStatus: number
  17 + sendTime: Date
  18 + sendMessageId: string
  19 + sendException: string
  20 +}
  21 +
  22 +export interface MailLogPageReqVO extends PageParam {
  23 + userId?: number
  24 + userType?: number
  25 + toMail?: string
  26 + accountId?: number
  27 + templateId?: number
  28 + sendStatus?: number
  29 + sendTime?: Date[]
  30 +}
  31 +
  32 +// 查询邮件日志列表
  33 +export const getMailLogPageApi = async (params: MailLogPageReqVO) => {
  34 + return await request.get({ url: '/system/mail-log/page', params })
  35 +}
  36 +
  37 +// 查询邮件日志详情
  38 +export const getMailLogApi = async (id: number) => {
  39 + return await request.get({ url: '/system/mail-log/get?id=' + id })
  40 +}
... ...
yudao-ui-admin-vue3/src/api/system/mail/template/index.ts 0 → 100644
  1 +import request from '@/config/axios'
  2 +
  3 +export interface MailTemplateVO {
  4 + id: number
  5 + name: string
  6 + code: string
  7 + accountId: number
  8 + nickname: string
  9 + title: string
  10 + content: string
  11 + params: string
  12 + status: number
  13 + remark: string
  14 +}
  15 +
  16 +export interface MailTemplatePageReqVO extends PageParam {
  17 + name?: string
  18 + code?: string
  19 + accountId?: number
  20 + status?: number
  21 + createTime?: Date[]
  22 +}
  23 +
  24 +export interface MailSmsReqVO {
  25 + mail: string
  26 + templateCode: string
  27 + templateParams: Map<String, Object>
  28 +}
  29 +
  30 +// 查询邮件模版列表
  31 +export const getMailTemplatePageApi = async (params: MailTemplatePageReqVO) => {
  32 + return await request.get({ url: '/system/mail-template/page', params })
  33 +}
  34 +
  35 +// 查询邮件模版详情
  36 +export const getMailTemplateApi = async (id: number) => {
  37 + return await request.get({ url: '/system/mail-template/get?id=' + id })
  38 +}
  39 +
  40 +// 新增邮件模版
  41 +export const createMailTemplateApi = async (data: MailTemplateVO) => {
  42 + return await request.post({ url: '/system/mail-template/create', data })
  43 +}
  44 +
  45 +// 修改邮件模版
  46 +export const updateMailTemplateApi = async (data: MailTemplateVO) => {
  47 + return await request.put({ url: '/system/mail-template/update', data })
  48 +}
  49 +
  50 +// 删除邮件模版
  51 +export const deleteMailTemplateApi = async (id: number) => {
  52 + return await request.delete({ url: '/system/mail-template/delete?id=' + id })
  53 +}
  54 +
  55 +// 发送邮件
  56 +export const sendMailApi = (data: MailSmsReqVO) => {
  57 + return request.post({ url: '/system/mail-template/send-mail', data })
  58 +}
... ...
yudao-ui-admin-vue3/src/components/XTable/src/XTable.vue
... ... @@ -255,16 +255,17 @@ const getToolBarConfig = (options: XTableProps) =&gt; {
255 255 if (toolbarConfig) return
256 256 if (toolBar) {
257 257 if (!isBoolean(toolBar)) {
  258 + console.info(2)
258 259 options.toolbarConfig = toolBar
259 260 return
260 261 }
261   - } else if (!topActionSlots) {
  262 + } else if (topActionSlots != false) {
262 263 options.toolbarConfig = {
263   - enabled: true
  264 + slots: { buttons: 'toolbar_buttons' }
264 265 }
265 266 } else {
266 267 options.toolbarConfig = {
267   - slots: { buttons: 'toolbar_buttons' }
  268 + enabled: true
268 269 }
269 270 }
270 271 }
... ...
yudao-ui-admin-vue3/src/main.ts
... ... @@ -37,6 +37,13 @@ import App from &#39;./App.vue&#39;
37 37  
38 38 import './permission'
39 39  
  40 +import { isDevMode } from '@/utils/env'
  41 +
  42 +if (isDevMode()) {
  43 + console.info(isDevMode())
  44 + import('element-plus/dist/index.css')
  45 +}
  46 +
40 47 // 创建实例
41 48 const setupAll = async () => {
42 49 const app = createApp(App)
... ...
yudao-ui-admin-vue3/src/types/auto-components.d.ts
... ... @@ -21,6 +21,7 @@ declare module &#39;@vue/runtime-core&#39; {
21 21 DictTag: typeof import('./../components/DictTag/src/DictTag.vue')['default']
22 22 Echart: typeof import('./../components/Echart/src/Echart.vue')['default']
23 23 Editor: typeof import('./../components/Editor/src/Editor.vue')['default']
  24 + ElAvatar: typeof import('element-plus/es')['ElAvatar']
24 25 ElBadge: typeof import('element-plus/es')['ElBadge']
25 26 ElButton: typeof import('element-plus/es')['ElButton']
26 27 ElCard: typeof import('element-plus/es')['ElCard']
... ... @@ -30,6 +31,7 @@ declare module &#39;@vue/runtime-core&#39; {
30 31 ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
31 32 ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
32 33 ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
  34 + ElDialog: typeof import('element-plus/es')['ElDialog']
33 35 ElDivider: typeof import('element-plus/es')['ElDivider']
34 36 ElDrawer: typeof import('element-plus/es')['ElDrawer']
35 37 ElDropdown: typeof import('element-plus/es')['ElDropdown']
... ... @@ -38,6 +40,7 @@ declare module &#39;@vue/runtime-core&#39; {
38 40 ElForm: typeof import('element-plus/es')['ElForm']
39 41 ElFormItem: typeof import('element-plus/es')['ElFormItem']
40 42 ElIcon: typeof import('element-plus/es')['ElIcon']
  43 + ElImage: typeof import('element-plus/es')['ElImage']
41 44 ElImageViewer: typeof import('element-plus/es')['ElImageViewer']
42 45 ElInput: typeof import('element-plus/es')['ElInput']
43 46 ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
... ... @@ -48,10 +51,14 @@ declare module &#39;@vue/runtime-core&#39; {
48 51 ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
49 52 ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
50 53 ElRow: typeof import('element-plus/es')['ElRow']
  54 + ElScroll: typeof import('element-plus/es')['ElScroll']
51 55 ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
52 56 ElSelect: typeof import('element-plus/es')['ElSelect']
53 57 ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
  58 + ElSpace: typeof import('element-plus/es')['ElSpace']
54 59 ElSwitch: typeof import('element-plus/es')['ElSwitch']
  60 + ElTable: typeof import('element-plus/es')['ElTable']
  61 + ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
55 62 ElTabPane: typeof import('element-plus/es')['ElTabPane']
56 63 ElTabs: typeof import('element-plus/es')['ElTabs']
57 64 ElTag: typeof import('element-plus/es')['ElTag']
... ...
yudao-ui-admin-vue3/src/utils/dict.ts
... ... @@ -90,6 +90,7 @@ export enum DICT_TYPE {
90 90 SYSTEM_SMS_RECEIVE_STATUS = 'system_sms_receive_status',
91 91 SYSTEM_ERROR_CODE_TYPE = 'system_error_code_type',
92 92 SYSTEM_OAUTH2_GRANT_TYPE = 'system_oauth2_grant_type',
  93 + SYSTEM_MAIL_SEND_STATUS = 'system_mail_send_status',
93 94  
94 95 // ========== INFRA 模块 ==========
95 96 INFRA_BOOLEAN_STRING = 'infra_boolean_string',
... ...
yudao-ui-admin-vue3/src/utils/env.ts 0 → 100644
  1 +export const isDevMode = () => {
  2 + const dev = import.meta.env.VITE_DEV
  3 + if (dev && dev === true) {
  4 + return true
  5 + } else {
  6 + return false
  7 + }
  8 +}
... ...
yudao-ui-admin-vue3/src/views/infra/swagger/index.vue
... ... @@ -5,5 +5,6 @@
5 5 </template>
6 6 <script setup lang="ts" name="Swagger">
7 7 const BASE_URL = import.meta.env.VITE_BASE_URL
8   -const src = ref(BASE_URL + '/doc.html')
  8 +// const src = ref(BASE_URL + '/doc.html')
  9 +const src = ref(BASE_URL + '/swagger-ui')
9 10 </script>
... ...
yudao-ui-admin-vue3/src/views/system/mail/account/account.data.ts 0 → 100644
  1 +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
  2 +
  3 +// 表单校验
  4 +export const rules = reactive({
  5 + mail: [required],
  6 + username: [required],
  7 + password: [required],
  8 + host: [required],
  9 + port: [required],
  10 + sslEnable: [required]
  11 +})
  12 +
  13 +// CrudSchema
  14 +const crudSchemas = reactive<VxeCrudSchema>({
  15 + primaryKey: 'id', // 默认的主键 ID
  16 + primaryTitle: '编号',
  17 + primaryType: 'id',
  18 + action: true,
  19 + actionWidth: '200', // 3 个按钮默认 200,如有删减对应增减即可
  20 + columns: [
  21 + {
  22 + title: '邮箱',
  23 + field: 'mail',
  24 + isSearch: true
  25 + },
  26 + {
  27 + title: '用户名',
  28 + field: 'username',
  29 + isSearch: true
  30 + },
  31 + {
  32 + title: '密码',
  33 + field: 'password',
  34 + isTable: false
  35 + },
  36 + {
  37 + title: 'SMTP 服务器域名',
  38 + field: 'host'
  39 + },
  40 + {
  41 + title: 'SMTP 服务器端口',
  42 + field: 'port',
  43 + form: {
  44 + component: 'InputNumber',
  45 + value: 465
  46 + }
  47 + },
  48 + {
  49 + title: '是否开启 SSL',
  50 + field: 'sslEnable',
  51 + dictType: DICT_TYPE.INFRA_BOOLEAN_STRING,
  52 + dictClass: 'boolean'
  53 + },
  54 + {
  55 + title: '创建时间',
  56 + field: 'createTime',
  57 + isForm: false,
  58 + formatter: 'formatDate',
  59 + table: {
  60 + width: 180
  61 + }
  62 + }
  63 + ]
  64 +})
  65 +export const { allSchemas } = useVxeCrudSchemas(crudSchemas)
... ...
yudao-ui-admin-vue3/src/views/system/mail/account/index.vue 0 → 100644
  1 +<template>
  2 + <ContentWrap>
  3 + <!-- 列表 -->
  4 + <XTable @register="registerTable">
  5 + <template #toolbar_buttons>
  6 + <!-- 操作:新增 -->
  7 + <XButton
  8 + type="primary"
  9 + preIcon="ep:zoom-in"
  10 + :title="t('action.add')"
  11 + v-hasPermi="['system:mail-account:create']"
  12 + @click="handleCreate()"
  13 + />
  14 + </template>
  15 + <template #actionbtns_default="{ row }">
  16 + <!-- 操作:修改 -->
  17 + <XTextButton
  18 + preIcon="ep:edit"
  19 + :title="t('action.edit')"
  20 + v-hasPermi="['system:mail-account:update']"
  21 + @click="handleUpdate(row.id)"
  22 + />
  23 + <!-- 操作:详情 -->
  24 + <XTextButton
  25 + preIcon="ep:view"
  26 + :title="t('action.detail')"
  27 + v-hasPermi="['system:mail-account:query']"
  28 + @click="handleDetail(row.id)"
  29 + />
  30 + <!-- 操作:删除 -->
  31 + <XTextButton
  32 + preIcon="ep:delete"
  33 + :title="t('action.del')"
  34 + v-hasPermi="['system:mail-account:delete']"
  35 + @click="deleteData(row.id)"
  36 + />
  37 + </template>
  38 + </XTable>
  39 + </ContentWrap>
  40 + <!-- 弹窗 -->
  41 + <XModal id="mailAccountModel" :loading="modelLoading" v-model="modelVisible" :title="modelTitle">
  42 + <!-- 表单:添加/修改 -->
  43 + <Form
  44 + ref="formRef"
  45 + v-if="['create', 'update'].includes(actionType)"
  46 + :schema="allSchemas.formSchema"
  47 + :rules="rules"
  48 + />
  49 + <!-- 表单:详情 -->
  50 + <Descriptions
  51 + v-if="actionType === 'detail'"
  52 + :schema="allSchemas.detailSchema"
  53 + :data="detailData"
  54 + />
  55 + <template #footer>
  56 + <!-- 按钮:保存 -->
  57 + <XButton
  58 + v-if="['create', 'update'].includes(actionType)"
  59 + type="primary"
  60 + :title="t('action.save')"
  61 + :loading="actionLoading"
  62 + @click="submitForm()"
  63 + />
  64 + <!-- 按钮:关闭 -->
  65 + <XButton :loading="actionLoading" :title="t('dialog.close')" @click="modelVisible = false" />
  66 + </template>
  67 + </XModal>
  68 +</template>
  69 +<script setup lang="ts" name="MailAccount">
  70 +import { FormExpose } from '@/components/Form'
  71 +// 业务相关的 import
  72 +import { rules, allSchemas } from './account.data'
  73 +import * as MailAccountApi from '@/api/system/mail/account'
  74 +
  75 +const { t } = useI18n() // 国际化
  76 +const message = useMessage() // 消息弹窗
  77 +
  78 +// 列表相关的变量
  79 +const [registerTable, { reload, deleteData }] = useXTable({
  80 + allSchemas: allSchemas,
  81 + getListApi: MailAccountApi.getMailAccountPageApi,
  82 + deleteApi: MailAccountApi.deleteMailAccountApi
  83 +})
  84 +
  85 +// 弹窗相关的变量
  86 +const modelVisible = ref(false) // 是否显示弹出层
  87 +const modelTitle = ref('edit') // 弹出层标题
  88 +const modelLoading = ref(false) // 弹出层loading
  89 +const actionType = ref('') // 操作按钮的类型
  90 +const actionLoading = ref(false) // 按钮 Loading
  91 +const formRef = ref<FormExpose>() // 表单 Ref
  92 +const detailData = ref() // 详情 Ref
  93 +
  94 +// 设置标题
  95 +const setDialogTile = (type: string) => {
  96 + modelLoading.value = true
  97 + modelTitle.value = t('action.' + type)
  98 + actionType.value = type
  99 + modelVisible.value = true
  100 +}
  101 +
  102 +// 新增操作
  103 +const handleCreate = () => {
  104 + setDialogTile('create')
  105 + modelLoading.value = false
  106 +}
  107 +
  108 +// 修改操作
  109 +const handleUpdate = async (rowId: number) => {
  110 + setDialogTile('update')
  111 + // 设置数据
  112 + const res = await MailAccountApi.getMailAccountApi(rowId)
  113 + unref(formRef)?.setValues(res)
  114 + modelLoading.value = false
  115 +}
  116 +
  117 +// 详情操作
  118 +const handleDetail = async (rowId: number) => {
  119 + setDialogTile('detail')
  120 + const res = await MailAccountApi.getMailAccountApi(rowId)
  121 + detailData.value = res
  122 + modelLoading.value = false
  123 +}
  124 +
  125 +// 提交按钮
  126 +const submitForm = async () => {
  127 + const elForm = unref(formRef)?.getElFormRef()
  128 + if (!elForm) return
  129 + elForm.validate(async (valid) => {
  130 + if (valid) {
  131 + actionLoading.value = true
  132 + // 提交请求
  133 + try {
  134 + const data = unref(formRef)?.formModel as MailAccountApi.MailAccountVO
  135 + if (actionType.value === 'create') {
  136 + await MailAccountApi.createMailAccountApi(data)
  137 + message.success(t('common.createSuccess'))
  138 + } else {
  139 + await MailAccountApi.updateMailAccountApi(data)
  140 + message.success(t('common.updateSuccess'))
  141 + }
  142 + modelVisible.value = false
  143 + } finally {
  144 + actionLoading.value = false
  145 + // 刷新列表
  146 + await reload()
  147 + }
  148 + }
  149 + })
  150 +}
  151 +</script>
... ...
yudao-ui-admin-vue3/src/views/system/mail/log/index.vue 0 → 100644
  1 +<template>
  2 + <ContentWrap>
  3 + <!-- 列表 -->
  4 + <XTable @register="registerTable">
  5 + <template #accountId_search>
  6 + <el-select v-model="queryParams.accountId">
  7 + <el-option :key="undefined" label="全部" :value="undefined" />
  8 + <el-option
  9 + v-for="item in accountOptions"
  10 + :key="item.id"
  11 + :label="item.mail"
  12 + :value="item.id"
  13 + />
  14 + </el-select>
  15 + </template>
  16 + <template #toMail_default="{ row }">
  17 + <div>{{ row.toMail }}</div>
  18 + <div v-if="row.userType && row.userId">
  19 + <dict-tag :type="DICT_TYPE.USER_TYPE" :value="row.userType" />{{ '(' + row.userId + ')' }}
  20 + </div>
  21 + </template>
  22 + <template #actionbtns_default="{ row }">
  23 + <!-- 操作:详情 -->
  24 + <XTextButton
  25 + preIcon="ep:view"
  26 + :title="t('action.detail')"
  27 + v-hasPermi="['system:mail-log:query']"
  28 + @click="handleDetail(row.id)"
  29 + />
  30 + </template>
  31 + </XTable>
  32 + </ContentWrap>
  33 + <!-- 弹窗 -->
  34 + <XModal id="mailLogModel" :loading="modelLoading" v-model="modelVisible" :title="modelTitle">
  35 + <!-- 表单:详情 -->
  36 + <Descriptions
  37 + v-if="actionType === 'detail'"
  38 + :schema="allSchemas.detailSchema"
  39 + :data="detailData"
  40 + />
  41 + <template #footer>
  42 + <!-- 按钮:关闭 -->
  43 + <XButton :loading="actionLoading" :title="t('dialog.close')" @click="modelVisible = false" />
  44 + </template>
  45 + </XModal>
  46 +</template>
  47 +<script setup lang="ts" name="MailLog">
  48 +// 业务相关的 import
  49 +import { allSchemas } from './log.data'
  50 +import * as MailLogApi from '@/api/system/mail/log'
  51 +import * as MailAccountApi from '@/api/system/mail/account'
  52 +
  53 +const { t } = useI18n() // 国际化
  54 +
  55 +// 列表相关的变量
  56 +const queryParams = reactive({
  57 + accountId: null
  58 +})
  59 +const [registerTable] = useXTable({
  60 + allSchemas: allSchemas,
  61 + params: queryParams,
  62 + getListApi: MailLogApi.getMailLogPageApi
  63 +})
  64 +const accountOptions = ref([]) // 账号下拉选项
  65 +
  66 +// 弹窗相关的变量
  67 +const modelVisible = ref(false) // 是否显示弹出层
  68 +const modelTitle = ref('edit') // 弹出层标题
  69 +const modelLoading = ref(false) // 弹出层loading
  70 +const actionType = ref('') // 操作按钮的类型
  71 +const actionLoading = ref(false) // 按钮 Loading
  72 +const detailData = ref() // 详情 Ref
  73 +
  74 +// 设置标题
  75 +const setDialogTile = (type: string) => {
  76 + modelLoading.value = true
  77 + modelTitle.value = t('action.' + type)
  78 + actionType.value = type
  79 + modelVisible.value = true
  80 +}
  81 +
  82 +// 详情操作
  83 +const handleDetail = async (rowId: number) => {
  84 + setDialogTile('detail')
  85 + const res = await MailLogApi.getMailLogApi(rowId)
  86 + detailData.value = res
  87 + modelLoading.value = false
  88 +}
  89 +
  90 +// ========== 初始化 ==========
  91 +onMounted(() => {
  92 + MailAccountApi.getSimpleMailAccounts().then((data) => {
  93 + accountOptions.value = data
  94 + })
  95 +})
  96 +</script>
... ...
yudao-ui-admin-vue3/src/views/system/mail/log/log.data.ts 0 → 100644
  1 +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
  2 +
  3 +// CrudSchema
  4 +const crudSchemas = reactive<VxeCrudSchema>({
  5 + primaryKey: 'id',
  6 + primaryTitle: '编号',
  7 + primaryType: 'id',
  8 + action: true,
  9 + actionWidth: '70',
  10 + columns: [
  11 + {
  12 + title: '发送时间',
  13 + field: 'sendTime',
  14 + table: {
  15 + width: 180
  16 + },
  17 + formatter: 'formatDate',
  18 + search: {
  19 + show: true,
  20 + itemRender: {
  21 + name: 'XDataTimePicker'
  22 + }
  23 + }
  24 + },
  25 + {
  26 + title: '接收邮箱',
  27 + field: 'toMail',
  28 + isSearch: true,
  29 + table: {
  30 + width: 180,
  31 + slots: {
  32 + default: 'toMail_default'
  33 + }
  34 + }
  35 + },
  36 + {
  37 + title: '用户编号',
  38 + field: 'userId',
  39 + isSearch: true,
  40 + isTable: false
  41 + },
  42 + {
  43 + title: '用户类型',
  44 + field: 'userType',
  45 + dictType: DICT_TYPE.USER_TYPE,
  46 + dictClass: 'number',
  47 + isSearch: true,
  48 + isTable: false
  49 + },
  50 + {
  51 + title: '邮件标题',
  52 + field: 'templateTitle'
  53 + },
  54 + {
  55 + title: '邮件内容',
  56 + field: 'templateContent',
  57 + isTable: false
  58 + },
  59 + {
  60 + title: '邮箱参数',
  61 + field: 'templateParams',
  62 + isTable: false
  63 + },
  64 + {
  65 + title: '发送状态',
  66 + field: 'sendStatus',
  67 + dictType: DICT_TYPE.SYSTEM_MAIL_SEND_STATUS,
  68 + dictClass: 'string',
  69 + isSearch: true
  70 + },
  71 + {
  72 + title: '邮箱账号',
  73 + field: 'accountId',
  74 + isSearch: true,
  75 + isTable: false,
  76 + search: {
  77 + slots: {
  78 + default: 'accountId_search'
  79 + }
  80 + }
  81 + },
  82 + {
  83 + title: '发送邮箱地址',
  84 + field: 'fromMail',
  85 + table: {
  86 + title: '邮箱账号'
  87 + }
  88 + },
  89 + {
  90 + title: '模板编号',
  91 + field: 'templateId',
  92 + isSearch: true
  93 + },
  94 + {
  95 + title: '模板编码',
  96 + field: 'templateCode',
  97 + isTable: false
  98 + },
  99 + {
  100 + title: '模版发送人名称',
  101 + field: 'templateNickname',
  102 + isTable: false
  103 + },
  104 + {
  105 + title: '发送返回的消息编号',
  106 + field: 'sendMessageId',
  107 + isTable: false
  108 + },
  109 + {
  110 + title: '发送异常',
  111 + field: 'sendException',
  112 + isTable: false
  113 + },
  114 + {
  115 + title: '创建时间',
  116 + field: 'createTime',
  117 + isTable: false
  118 + }
  119 + ]
  120 +})
  121 +export const { allSchemas } = useVxeCrudSchemas(crudSchemas)
... ...
yudao-ui-admin-vue3/src/views/system/mail/template/index.vue 0 → 100644
  1 +<template>
  2 + <ContentWrap>
  3 + <!-- 列表 -->
  4 + <XTable @register="registerTable">
  5 + <template #accountId_search>
  6 + <el-select v-model="queryParams.accountId">
  7 + <el-option :key="undefined" label="全部" :value="undefined" />
  8 + <el-option
  9 + v-for="item in accountOptions"
  10 + :key="item.id"
  11 + :label="item.mail"
  12 + :value="item.id"
  13 + />
  14 + </el-select>
  15 + </template>
  16 + <template #toolbar_buttons>
  17 + <!-- 操作:新增 -->
  18 + <XButton
  19 + type="primary"
  20 + preIcon="ep:zoom-in"
  21 + :title="t('action.add')"
  22 + v-hasPermi="['system:mail-template:create']"
  23 + @click="handleCreate()"
  24 + />
  25 + </template>
  26 + <template #accountId_default="{ row }">
  27 + <span>{{ accountOptions.find((account) => account.id === row.accountId)?.mail }}</span>
  28 + </template>
  29 + <template #actionbtns_default="{ row }">
  30 + <!-- 操作:测试短信 -->
  31 + <XTextButton
  32 + preIcon="ep:cpu"
  33 + :title="t('action.test')"
  34 + v-hasPermi="['system:mail-template:send-mail']"
  35 + @click="handleSendMail(row)"
  36 + />
  37 + <!-- 操作:修改 -->
  38 + <XTextButton
  39 + preIcon="ep:edit"
  40 + :title="t('action.edit')"
  41 + v-hasPermi="['system:mail-template:update']"
  42 + @click="handleUpdate(row.id)"
  43 + />
  44 + <!-- 操作:详情 -->
  45 + <XTextButton
  46 + preIcon="ep:view"
  47 + :title="t('action.detail')"
  48 + v-hasPermi="['system:mail-template:query']"
  49 + @click="handleDetail(row.id)"
  50 + />
  51 + <!-- 操作:删除 -->
  52 + <XTextButton
  53 + preIcon="ep:delete"
  54 + :title="t('action.del')"
  55 + v-hasPermi="['system:mail-template:delete']"
  56 + @click="deleteData(row.id)"
  57 + />
  58 + </template>
  59 + </XTable>
  60 + </ContentWrap>
  61 +
  62 + <!-- 添加/修改/详情的弹窗 -->
  63 + <XModal id="mailTemplateModel" :loading="modelLoading" v-model="modelVisible" :title="modelTitle">
  64 + <!-- 表单:添加/修改 -->
  65 + <Form
  66 + ref="formRef"
  67 + v-if="['create', 'update'].includes(actionType)"
  68 + :schema="allSchemas.formSchema"
  69 + :rules="rules"
  70 + >
  71 + <template #accountId="form">
  72 + <el-select v-model="form.accountId">
  73 + <el-option
  74 + v-for="item in accountOptions"
  75 + :key="item.id"
  76 + :label="item.mail"
  77 + :value="item.id"
  78 + />
  79 + </el-select>
  80 + </template>
  81 + </Form>
  82 + <!-- 表单:详情 -->
  83 + <Descriptions
  84 + v-if="actionType === 'detail'"
  85 + :schema="allSchemas.detailSchema"
  86 + :data="detailData"
  87 + />
  88 + <template #footer>
  89 + <!-- 按钮:保存 -->
  90 + <XButton
  91 + v-if="['create', 'update'].includes(actionType)"
  92 + type="primary"
  93 + :title="t('action.save')"
  94 + :loading="actionLoading"
  95 + @click="submitForm()"
  96 + />
  97 + <!-- 按钮:关闭 -->
  98 + <XButton :loading="actionLoading" :title="t('dialog.close')" @click="modelVisible = false" />
  99 + </template>
  100 + </XModal>
  101 +
  102 + <!-- 测试邮件的弹窗 -->
  103 + <XModal id="sendTest" v-model="sendVisible" title="测试">
  104 + <el-form :model="sendForm" :rules="sendRules" label-width="200px" label-position="top">
  105 + <el-form-item label="模板内容" prop="content">
  106 + <Editor :model-value="sendForm.content" readonly height="150px" />
  107 + </el-form-item>
  108 + <el-form-item label="收件邮箱" prop="mail">
  109 + <el-input v-model="sendForm.mail" placeholder="请输入收件邮箱" />
  110 + </el-form-item>
  111 + <el-form-item
  112 + v-for="param in sendForm.params"
  113 + :key="param"
  114 + :label="'参数 {' + param + '}'"
  115 + :prop="'templateParams.' + param"
  116 + >
  117 + <el-input
  118 + v-model="sendForm.templateParams[param]"
  119 + :placeholder="'请输入 ' + param + ' 参数'"
  120 + />
  121 + </el-form-item>
  122 + </el-form>
  123 + <!-- 操作按钮 -->
  124 + <template #footer>
  125 + <XButton
  126 + type="primary"
  127 + :title="t('action.test')"
  128 + :loading="actionLoading"
  129 + @click="sendTest()"
  130 + />
  131 + <XButton :title="t('dialog.close')" @click="sendVisible = false" />
  132 + </template>
  133 + </XModal>
  134 +</template>
  135 +<script setup lang="ts" name="MailTemplate">
  136 +import { FormExpose } from '@/components/Form'
  137 +// 业务相关的 import
  138 +import { rules, allSchemas } from './template.data'
  139 +import * as MailTemplateApi from '@/api/system/mail/template'
  140 +import * as MailAccountApi from '@/api/system/mail/account'
  141 +
  142 +const { t } = useI18n() // 国际化
  143 +const message = useMessage() // 消息弹窗
  144 +
  145 +// 列表相关的变量
  146 +const queryParams = reactive({
  147 + accountId: null
  148 +})
  149 +const [registerTable, { reload, deleteData }] = useXTable({
  150 + allSchemas: allSchemas,
  151 + params: queryParams,
  152 + getListApi: MailTemplateApi.getMailTemplatePageApi,
  153 + deleteApi: MailTemplateApi.deleteMailTemplateApi
  154 +})
  155 +const accountOptions = ref([]) // 账号下拉选项
  156 +
  157 +// 弹窗相关的变量
  158 +const modelVisible = ref(false) // 是否显示弹出层
  159 +const modelTitle = ref('edit') // 弹出层标题
  160 +const modelLoading = ref(false) // 弹出层loading
  161 +const actionType = ref('') // 操作按钮的类型
  162 +const actionLoading = ref(false) // 按钮 Loading
  163 +const formRef = ref<FormExpose>() // 表单 Ref
  164 +const detailData = ref() // 详情 Ref
  165 +
  166 +// 设置标题
  167 +const setDialogTile = (type: string) => {
  168 + modelLoading.value = true
  169 + modelTitle.value = t('action.' + type)
  170 + actionType.value = type
  171 + modelVisible.value = true
  172 +}
  173 +
  174 +// 新增操作
  175 +const handleCreate = () => {
  176 + setDialogTile('create')
  177 + modelLoading.value = false
  178 +}
  179 +
  180 +// 修改操作
  181 +const handleUpdate = async (rowId: number) => {
  182 + setDialogTile('update')
  183 + // 设置数据
  184 + const res = await MailTemplateApi.getMailTemplateApi(rowId)
  185 + unref(formRef)?.setValues(res)
  186 + modelLoading.value = false
  187 +}
  188 +
  189 +// 详情操作
  190 +const handleDetail = async (rowId: number) => {
  191 + setDialogTile('detail')
  192 + const res = await MailTemplateApi.getMailTemplateApi(rowId)
  193 + detailData.value = res
  194 + modelLoading.value = false
  195 +}
  196 +
  197 +// 提交按钮
  198 +const submitForm = async () => {
  199 + const elForm = unref(formRef)?.getElFormRef()
  200 + if (!elForm) return
  201 + elForm.validate(async (valid) => {
  202 + if (valid) {
  203 + actionLoading.value = true
  204 + // 提交请求
  205 + try {
  206 + const data = unref(formRef)?.formModel as MailTemplateApi.MailTemplateVO
  207 + if (actionType.value === 'create') {
  208 + await MailTemplateApi.createMailTemplateApi(data)
  209 + message.success(t('common.createSuccess'))
  210 + } else {
  211 + await MailTemplateApi.updateMailTemplateApi(data)
  212 + message.success(t('common.updateSuccess'))
  213 + }
  214 + modelVisible.value = false
  215 + } finally {
  216 + actionLoading.value = false
  217 + // 刷新列表
  218 + await reload()
  219 + }
  220 + }
  221 + })
  222 +}
  223 +
  224 +// ========== 测试相关 ==========
  225 +const sendForm = ref({
  226 + content: '',
  227 + params: {},
  228 + mail: '',
  229 + templateCode: '',
  230 + templateParams: {}
  231 +})
  232 +const sendRules = ref({
  233 + mail: [{ required: true, message: '邮箱不能为空', trigger: 'blur' }],
  234 + templateCode: [{ required: true, message: '模版编号不能为空', trigger: 'blur' }],
  235 + templateParams: {}
  236 +})
  237 +const sendVisible = ref(false)
  238 +
  239 +const handleSendMail = (row: any) => {
  240 + sendForm.value.content = row.content
  241 + sendForm.value.params = row.params
  242 + sendForm.value.templateCode = row.code
  243 + sendForm.value.templateParams = row.params.reduce(function (obj, item) {
  244 + obj[item] = undefined
  245 + return obj
  246 + }, {})
  247 + sendRules.value.templateParams = row.params.reduce(function (obj, item) {
  248 + obj[item] = { required: true, message: '参数 ' + item + ' 不能为空', trigger: 'change' }
  249 + return obj
  250 + }, {})
  251 + sendVisible.value = true
  252 +}
  253 +
  254 +const sendTest = async () => {
  255 + const data: MailTemplateApi.MailSmsReqVO = {
  256 + mail: sendForm.value.mail,
  257 + templateCode: sendForm.value.templateCode,
  258 + templateParams: sendForm.value.templateParams as unknown as Map<string, Object>
  259 + }
  260 + const res = await MailTemplateApi.sendMailApi(data)
  261 + if (res) {
  262 + message.success('提交发送成功!发送结果,见发送日志编号:' + res)
  263 + }
  264 + sendVisible.value = false
  265 +}
  266 +
  267 +// ========== 初始化 ==========
  268 +onMounted(() => {
  269 + MailAccountApi.getSimpleMailAccounts().then((data) => {
  270 + accountOptions.value = data
  271 + })
  272 +})
  273 +</script>
... ...
yudao-ui-admin-vue3/src/views/system/mail/template/template.data.ts 0 → 100644
  1 +import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
  2 +
  3 +// 表单校验
  4 +export const rules = reactive({
  5 + name: [required],
  6 + code: [required],
  7 + accountId: [required],
  8 + title: [required],
  9 + content: [required],
  10 + params: [required],
  11 + status: [required]
  12 +})
  13 +
  14 +// CrudSchema
  15 +const crudSchemas = reactive<VxeCrudSchema>({
  16 + primaryKey: 'id', // 默认的主键ID
  17 + primaryTitle: '编号', // 默认显示的值
  18 + primaryType: null,
  19 + action: true,
  20 + actionWidth: '260',
  21 + columns: [
  22 + {
  23 + title: '模板编码',
  24 + field: 'code',
  25 + isSearch: true
  26 + },
  27 + {
  28 + title: '模板名称',
  29 + field: 'name',
  30 + isSearch: true
  31 + },
  32 + {
  33 + title: '模板标题',
  34 + field: 'title'
  35 + },
  36 + {
  37 + title: '模板内容',
  38 + field: 'content',
  39 + form: {
  40 + component: 'Editor',
  41 + colProps: {
  42 + span: 24
  43 + },
  44 + componentProps: {
  45 + valueHtml: ''
  46 + }
  47 + }
  48 + },
  49 + {
  50 + title: '邮箱账号',
  51 + field: 'accountId',
  52 + isSearch: true,
  53 + table: {
  54 + width: 200,
  55 + slots: {
  56 + default: 'accountId_default'
  57 + }
  58 + },
  59 + search: {
  60 + slots: {
  61 + default: 'accountId_search'
  62 + }
  63 + }
  64 + },
  65 + {
  66 + title: '发送人名称',
  67 + field: 'nickname'
  68 + },
  69 + {
  70 + title: '开启状态',
  71 + field: 'status',
  72 + isSearch: true,
  73 + dictType: DICT_TYPE.COMMON_STATUS,
  74 + dictClass: 'number'
  75 + },
  76 + {
  77 + title: '备注',
  78 + field: 'remark',
  79 + isTable: false
  80 + },
  81 + {
  82 + title: '创建时间',
  83 + field: 'createTime',
  84 + isForm: false,
  85 + formatter: 'formatDate',
  86 + table: {
  87 + width: 180
  88 + },
  89 + search: {
  90 + show: true,
  91 + itemRender: {
  92 + name: 'XDataTimePicker'
  93 + }
  94 + }
  95 + }
  96 + ]
  97 +})
  98 +export const { allSchemas } = useVxeCrudSchemas(crudSchemas)
... ...
yudao-ui-admin-vue3/src/views/system/sms/smsTemplate/index.vue
... ... @@ -197,7 +197,7 @@ const sendSmsForm = ref({
197 197 })
198 198 const sendSmsRules = ref({
199 199 mobile: [{ required: true, message: '手机不能为空', trigger: 'blur' }],
200   - templateCode: [{ required: true, message: '手机不能为空', trigger: 'blur' }],
  200 + templateCode: [{ required: true, message: '模版编号不能为空', trigger: 'blur' }],
201 201 templateParams: {}
202 202 })
203 203 const sendVisible = ref(false)
... ... @@ -225,7 +225,7 @@ const sendSmsTest = async () =&gt; {
225 225 }
226 226 const res = await SmsTemplateApi.sendSmsApi(data)
227 227 if (res) {
228   - message.success('发送成功')
  228 + message.success('提交发送成功!发送结果,见发送日志编号:' + res)
229 229 }
230 230 sendVisible.value = false
231 231 }
... ...
yudao-ui-admin-vue3/src/views/system/user/index.vue
... ... @@ -8,17 +8,19 @@
8 8 </div>
9 9 </template>
10 10 <el-input v-model="filterText" placeholder="搜索部门" />
11   - <el-tree
12   - ref="treeRef"
13   - node-key="id"
14   - default-expand-all
15   - :data="deptOptions"
16   - :props="defaultProps"
17   - :highlight-current="true"
18   - :filter-node-method="filterNode"
19   - :expand-on-click-node="false"
20   - @node-click="handleDeptNodeClick"
21   - />
  11 + <el-scrollbar height="650">
  12 + <el-tree
  13 + ref="treeRef"
  14 + node-key="id"
  15 + default-expand-all
  16 + :data="deptOptions"
  17 + :props="defaultProps"
  18 + :highlight-current="true"
  19 + :filter-node-method="filterNode"
  20 + :expand-on-click-node="false"
  21 + @node-click="handleDeptNodeClick"
  22 + />
  23 + </el-scrollbar>
22 24 </el-card>
23 25 <el-card class="w-4/5 user" style="margin-left: 10px" :gutter="12" shadow="hover">
24 26 <template #header>
... ... @@ -534,8 +536,8 @@ onMounted(async () =&gt; {
534 536  
535 537 <style scoped>
536 538 .user {
537   - height: 900px;
538   - max-height: 960px;
  539 + height: 780px;
  540 + max-height: 800px;
539 541 }
540 542 .card-header {
541 543 display: flex;
... ...
yudao-ui-admin-vue3/types/env.d.ts
... ... @@ -11,6 +11,7 @@ interface ImportMetaEnv {
11 11 readonly VITE_APP_TITLE: string
12 12 readonly VITE_PORT: number
13 13 readonly VITE_OPEN: string
  14 + readonly VITE_DEV: boolean
14 15 readonly VITE_ROUTE_ALWAYSSHOW_ENABLE: string
15 16 readonly VITE_APP_CAPTCHA_ENABLE: string
16 17 readonly VITE_APP_TENANT_ENABLE: string
... ...
yudao-ui-admin/package.json
... ... @@ -49,7 +49,7 @@
49 49 "core-js": "^3.26.0",
50 50 "crypto-js": "^4.0.0",
51 51 "echarts": "5.4.0",
52   - "element-ui": "2.15.10",
  52 + "element-ui": "2.15.12",
53 53 "file-saver": "2.0.5",
54 54 "fuse.js": "6.6.2",
55 55 "highlight.js": "9.18.5",
... ...
yudao-ui-admin/src/api/system/mail/account.js 0 → 100755
  1 +import request from '@/utils/request'
  2 +
  3 +// 创建邮箱账号
  4 +export function createMailAccount(data) {
  5 + return request({
  6 + url: '/system/mail-account/create',
  7 + method: 'post',
  8 + data: data
  9 + })
  10 +}
  11 +
  12 +// 更新邮箱账号
  13 +export function updateMailAccount(data) {
  14 + return request({
  15 + url: '/system/mail-account/update',
  16 + method: 'put',
  17 + data: data
  18 + })
  19 +}
  20 +
  21 +// 删除邮箱账号
  22 +export function deleteMailAccount(id) {
  23 + return request({
  24 + url: '/system/mail-account/delete?id=' + id,
  25 + method: 'delete'
  26 + })
  27 +}
  28 +
  29 +// 获得邮箱账号
  30 +export function getMailAccount(id) {
  31 + return request({
  32 + url: '/system/mail-account/get?id=' + id,
  33 + method: 'get'
  34 + })
  35 +}
  36 +
  37 +// 获得邮箱账号分页
  38 +export function getMailAccountPage(query) {
  39 + return request({
  40 + url: '/system/mail-account/page',
  41 + method: 'get',
  42 + params: query
  43 + })
  44 +}
  45 +
  46 +// 获取邮箱账号的精简信息列表
  47 +export function getSimpleMailAccountList() {
  48 + return request({
  49 + url: '/system/mail-account/list-all-simple',
  50 + method: 'get',
  51 + })
  52 +}
... ...
yudao-ui-admin/src/api/system/mail/log.js 0 → 100755
  1 +import request from '@/utils/request'
  2 +
  3 +// 获得邮件日志
  4 +export function getMailLog(id) {
  5 + return request({
  6 + url: '/system/mail-log/get?id=' + id,
  7 + method: 'get'
  8 + })
  9 +}
  10 +
  11 +// 获得邮件日志分页
  12 +export function getMailLogPage(query) {
  13 + return request({
  14 + url: '/system/mail-log/page',
  15 + method: 'get',
  16 + params: query
  17 + })
  18 +}
... ...
yudao-ui-admin/src/api/system/mail/template.js 0 → 100755
  1 +import request from '@/utils/request'
  2 +
  3 +// 创建邮件模版
  4 +export function createMailTemplate(data) {
  5 + return request({
  6 + url: '/system/mail-template/create',
  7 + method: 'post',
  8 + data: data
  9 + })
  10 +}
  11 +
  12 +// 更新邮件模版
  13 +export function updateMailTemplate(data) {
  14 + return request({
  15 + url: '/system/mail-template/update',
  16 + method: 'put',
  17 + data: data
  18 + })
  19 +}
  20 +
  21 +// 删除邮件模版
  22 +export function deleteMailTemplate(id) {
  23 + return request({
  24 + url: '/system/mail-template/delete?id=' + id,
  25 + method: 'delete'
  26 + })
  27 +}
  28 +
  29 +// 获得邮件模版
  30 +export function getMailTemplate(id) {
  31 + return request({
  32 + url: '/system/mail-template/get?id=' + id,
  33 + method: 'get'
  34 + })
  35 +}
  36 +
  37 +// 获得邮件模版分页
  38 +export function getMailTemplatePage(query) {
  39 + return request({
  40 + url: '/system/mail-template/page',
  41 + method: 'get',
  42 + params: query
  43 + })
  44 +}
  45 +
  46 +// 发送测试邮件
  47 +export function sendMail(data) {
  48 + return request({
  49 + url: '/system/mail-template/send-mail',
  50 + method: 'post',
  51 + data: data
  52 + })
  53 +}
... ...
yudao-ui-admin/src/api/system/sms/smsTemplate.js
... ... @@ -43,7 +43,7 @@ export function getSmsTemplatePage(query) {
43 43 })
44 44 }
45 45  
46   -// 创建短信模板
  46 +// 发送测试短信
47 47 export function sendSms(data) {
48 48 return request({
49 49 url: '/system/sms-template/send-sms',
... ...
yudao-ui-admin/src/components/TopNav/index.vue
... ... @@ -133,7 +133,9 @@ export default {
133 133 } else {
134 134 // 显示左侧联动菜单
135 135 this.activeRoutes(key);
136   - this.$store.dispatch('app/toggleSideBarHide', false);
  136 + if (!this.$route.meta.link) {
  137 + this.$store.dispatch('app/toggleSideBarHide', false);
  138 + }
137 139 }
138 140 },
139 141 // 当前激活的路由
... ... @@ -149,7 +151,7 @@ export default {
149 151 if(routes.length > 0) {
150 152 this.$store.commit("SET_SIDEBAR_ROUTERS", routes);
151 153 } else {
152   - this.$store.dispatch("app/toggleSideBarHide", true);
  154 + this.$store.dispatch('app/toggleSideBarHide', true);
153 155 }
154 156 },
155 157 ishttp(url) {
... ...
yudao-ui-admin/src/utils/dict.js
... ... @@ -25,6 +25,7 @@ export const DICT_TYPE = {
25 25 SYSTEM_SMS_RECEIVE_STATUS: 'system_sms_receive_status',
26 26 SYSTEM_ERROR_CODE_TYPE: 'system_error_code_type',
27 27 SYSTEM_OAUTH2_GRANT_TYPE: 'system_oauth2_grant_type',
  28 + SYSTEM_MAIL_SEND_STATUS: 'system_mail_send_status',
28 29  
29 30 // ========== INFRA 模块 ==========
30 31 INFRA_BOOLEAN_STRING: 'infra_boolean_string',
... ...
yudao-ui-admin/src/views/system/mail/account/index.vue 0 → 100755
  1 +<template>
  2 + <div class="app-container">
  3 + <doc-alert title="邮件配置" url="https://doc.iocoder.cn/mail" />
  4 +
  5 + <!-- 搜索工作栏 -->
  6 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  7 + <el-form-item label="邮箱" prop="mail">
  8 + <el-input v-model="queryParams.mail" placeholder="请输入邮箱" clearable @keyup.enter.native="handleQuery"/>
  9 + </el-form-item>
  10 + <el-form-item label="用户名" prop="username">
  11 + <el-input v-model="queryParams.username" placeholder="请输入用户名" clearable @keyup.enter.native="handleQuery"/>
  12 + </el-form-item>
  13 + <el-form-item>
  14 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  15 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  16 + </el-form-item>
  17 + </el-form>
  18 +
  19 + <!-- 操作工具栏 -->
  20 + <el-row :gutter="10" class="mb8">
  21 + <el-col :span="1.5">
  22 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
  23 + v-hasPermi="['system:mail-account:create']">新增</el-button>
  24 + </el-col>
  25 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  26 + </el-row>
  27 +
  28 + <!-- 列表 -->
  29 + <el-table v-loading="loading" :data="list">
  30 + <el-table-column label="编号" align="center" prop="id" />
  31 + <el-table-column label="邮箱" align="center" prop="mail" />
  32 + <el-table-column label="用户名" align="center" prop="username" />
  33 + <el-table-column label="SMTP 服务器域名" align="center" prop="host" />
  34 + <el-table-column label="SMTP 服务器端口" align="center" prop="port" />
  35 + <el-table-column label="是否开启 SSL" align="center" prop="sslEnable">
  36 + <template v-slot="scope">
  37 + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.sslEnable" />
  38 + </template>
  39 + </el-table-column>
  40 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  41 + <template v-slot="scope">
  42 + <span>{{ parseTime(scope.row.createTime) }}</span>
  43 + </template>
  44 + </el-table-column>
  45 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  46 + <template v-slot="scope">
  47 + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
  48 + v-hasPermi="['system:mail-account:update']">修改</el-button>
  49 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  50 + v-hasPermi="['system:mail-account:delete']">删除</el-button>
  51 + </template>
  52 + </el-table-column>
  53 + </el-table>
  54 + <!-- 分页组件 -->
  55 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  56 + @pagination="getList"/>
  57 +
  58 + <!-- 对话框(添加 / 修改) -->
  59 + <el-dialog :title="title" :visible.sync="open" width="500px" v-dialogDrag append-to-body>
  60 + <el-form ref="form" :model="form" :rules="rules" label-width="140px">
  61 + <el-form-item label="邮箱" prop="mail">
  62 + <el-input v-model="form.mail" placeholder="请输入邮箱" />
  63 + </el-form-item>
  64 + <el-form-item label="用户名" prop="username">
  65 + <el-input v-model="form.username" placeholder="请输入用户名,一般和邮箱一致" />
  66 + </el-form-item>
  67 + <el-form-item label="密码" prop="password">
  68 + <el-input v-model="form.password" placeholder="请输入密码" />
  69 + </el-form-item>
  70 + <el-form-item label="SMTP 服务器域名" prop="host">
  71 + <el-input v-model="form.host" placeholder="请输入 SMTP 服务器域名" />
  72 + </el-form-item>
  73 + <el-form-item label="SMTP 服务器端口" prop="port">
  74 + <el-input v-model="form.port" placeholder="请输入 SMTP 服务器端口" />
  75 + </el-form-item>
  76 + <el-form-item label="是否开启 SSL" prop="sslEnable">
  77 + <el-radio-group v-model="form.sslEnable">
  78 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
  79 + :key="dict.value" :label="dict.value === 'true'">{{dict.label}}</el-radio>
  80 + </el-radio-group>
  81 + </el-form-item>
  82 + </el-form>
  83 + <div slot="footer" class="dialog-footer">
  84 + <el-button type="primary" @click="submitForm">确 定</el-button>
  85 + <el-button @click="cancel">取 消</el-button>
  86 + </div>
  87 + </el-dialog>
  88 + </div>
  89 +</template>
  90 +
  91 +<script>
  92 +import { createMailAccount, updateMailAccount, deleteMailAccount, getMailAccount, getMailAccountPage } from "@/api/system/mail/account";
  93 +
  94 +export default {
  95 + name: "MailAccount",
  96 + components: {
  97 + },
  98 + data() {
  99 + return {
  100 + // 遮罩层
  101 + loading: true,
  102 + // 显示搜索条件
  103 + showSearch: true,
  104 + // 总条数
  105 + total: 0,
  106 + // 邮箱账号列表
  107 + list: [],
  108 + // 弹出层标题
  109 + title: "",
  110 + // 是否显示弹出层
  111 + open: false,
  112 + // 查询参数
  113 + queryParams: {
  114 + pageNo: 1,
  115 + pageSize: 10,
  116 + mail: null,
  117 + username: null
  118 + },
  119 + // 表单参数
  120 + form: {},
  121 + // 表单校验
  122 + rules: {
  123 + mail: [{ required: true, message: "邮箱不能为空", trigger: "blur" }],
  124 + username: [{ required: true, message: "用户名不能为空", trigger: "blur" }],
  125 + password: [{ required: true, message: "密码不能为空", trigger: "blur" }],
  126 + host: [{ required: true, message: "SMTP 服务器域名不能为空", trigger: "blur" }],
  127 + port: [{ required: true, message: "SMTP 服务器端口不能为空", trigger: "blur" }],
  128 + sslEnable: [{ required: true, message: "是否开启 SSL不能为空", trigger: "blur" }],
  129 + },
  130 + };
  131 + },
  132 + created() {
  133 + this.getList();
  134 + },
  135 + methods: {
  136 + /** 查询列表 */
  137 + getList() {
  138 + this.loading = true;
  139 + // 执行查询
  140 + getMailAccountPage(this.queryParams).then(response => {
  141 + this.list = response.data.list;
  142 + this.total = response.data.total;
  143 + this.loading = false;
  144 + });
  145 + },
  146 + /** 取消按钮 */
  147 + cancel() {
  148 + this.open = false;
  149 + this.reset();
  150 + },
  151 + /** 表单重置 */
  152 + reset() {
  153 + this.form = {
  154 + id: undefined,
  155 + mail: undefined,
  156 + username: undefined,
  157 + password: undefined,
  158 + host: undefined,
  159 + port: undefined,
  160 + sslEnable: true,
  161 + };
  162 + this.resetForm("form");
  163 + },
  164 + /** 搜索按钮操作 */
  165 + handleQuery() {
  166 + this.queryParams.pageNo = 1;
  167 + this.getList();
  168 + },
  169 + /** 重置按钮操作 */
  170 + resetQuery() {
  171 + this.resetForm("queryForm");
  172 + this.handleQuery();
  173 + },
  174 + /** 新增按钮操作 */
  175 + handleAdd() {
  176 + this.reset();
  177 + this.open = true;
  178 + this.title = "添加邮箱账号";
  179 + },
  180 + /** 修改按钮操作 */
  181 + handleUpdate(row) {
  182 + this.reset();
  183 + const id = row.id;
  184 + getMailAccount(id).then(response => {
  185 + this.form = response.data;
  186 + this.open = true;
  187 + this.title = "修改邮箱账号";
  188 + });
  189 + },
  190 + /** 提交按钮 */
  191 + submitForm() {
  192 + this.$refs["form"].validate(valid => {
  193 + if (!valid) {
  194 + return;
  195 + }
  196 + // 修改的提交
  197 + if (this.form.id != null) {
  198 + updateMailAccount(this.form).then(response => {
  199 + this.$modal.msgSuccess("修改成功");
  200 + this.open = false;
  201 + this.getList();
  202 + });
  203 + return;
  204 + }
  205 + // 添加的提交
  206 + createMailAccount(this.form).then(response => {
  207 + this.$modal.msgSuccess("新增成功");
  208 + this.open = false;
  209 + this.getList();
  210 + });
  211 + });
  212 + },
  213 + /** 删除按钮操作 */
  214 + handleDelete(row) {
  215 + const id = row.id;
  216 + this.$modal.confirm('是否确认删除邮箱账号编号为"' + id + '"的数据项?').then(function() {
  217 + return deleteMailAccount(id);
  218 + }).then(() => {
  219 + this.getList();
  220 + this.$modal.msgSuccess("删除成功");
  221 + }).catch(() => {});
  222 + }
  223 + }
  224 +};
  225 +</script>
... ...
yudao-ui-admin/src/views/system/mail/log/index.vue 0 → 100755
  1 +<template>
  2 + <div class="app-container">
  3 + <doc-alert title="邮件配置" url="https://doc.iocoder.cn/mail" />
  4 +
  5 + <!-- 搜索工作栏 -->
  6 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  7 + <el-form-item label="接收邮箱" prop="toMail">
  8 + <el-input v-model="queryParams.toMail" placeholder="请输入接收邮箱" clearable @keyup.enter.native="handleQuery"/>
  9 + </el-form-item>
  10 + <el-form-item label="邮箱账号" prop="accountId">
  11 + <el-select v-model="queryParams.accountId" placeholder="请输入邮箱账号" clearable>
  12 + <el-option v-for="account in accountOptions" :key="account.id" :value="account.id" :label="account.mail" />
  13 + </el-select>
  14 + </el-form-item>
  15 + <el-form-item label="模板编号" prop="templateId">
  16 + <el-input v-model="queryParams.templateId" placeholder="请输入模板编号" clearable @keyup.enter.native="handleQuery"/>
  17 + </el-form-item>
  18 + <el-form-item label="发送状态" prop="sendStatus">
  19 + <el-select v-model="queryParams.sendStatus" placeholder="请选择发送状态" clearable size="small">
  20 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_MAIL_SEND_STATUS)"
  21 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  22 + </el-select>
  23 + </el-form-item>
  24 + <el-form-item label="发送时间" prop="sendTime">
  25 + <el-date-picker v-model="queryParams.sendTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
  26 + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
  27 + </el-form-item>
  28 + <el-form-item label="用户编号" prop="userId">
  29 + <el-input v-model="queryParams.userId" placeholder="请输入用户编号" clearable @keyup.enter.native="handleQuery"/>
  30 + </el-form-item>
  31 + <el-form-item label="用户类型" prop="userType">
  32 + <el-select v-model="queryParams.userType" placeholder="请选择用户类型" clearable>
  33 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.USER_TYPE)"
  34 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  35 + </el-select>
  36 + </el-form-item>
  37 + <el-form-item>
  38 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  39 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  40 + </el-form-item>
  41 + </el-form>
  42 +
  43 + <!-- 操作工具栏 -->
  44 + <el-row :gutter="10" class="mb8">
  45 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  46 + </el-row>
  47 +
  48 + <!-- 列表 -->
  49 + <el-table v-loading="loading" :data="list">
  50 + <el-table-column label="编号" align="center" prop="id" />
  51 + <el-table-column label="发送时间" align="center" prop="sendTime" width="180">
  52 + <template v-slot="scope">
  53 + <span>{{ parseTime(scope.row.sendTime) }}</span>
  54 + </template>
  55 + </el-table-column>
  56 + <el-table-column label="接收邮箱" align="center" prop="toMail" width="200">
  57 + <template v-slot="scope">
  58 + <div>{{ scope.row.toMail }}</div>
  59 + <div v-if="scope.row.userType && scope.row.userId">
  60 + <dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType"/>{{ '(' + scope.row.userId + ')' }}
  61 + </div>
  62 + </template>
  63 + </el-table-column>
  64 + <el-table-column label="邮件标题" align="center" prop="templateTitle" />
  65 + <el-table-column label="发送状态" align="center" prop="sendStatus">
  66 + <template v-slot="scope">
  67 + <dict-tag :type="DICT_TYPE.SYSTEM_MAIL_SEND_STATUS" :value="scope.row.sendStatus" />
  68 + </template>
  69 + </el-table-column>
  70 + <el-table-column label="邮箱账号" align="center" prop="fromMail" />
  71 + <el-table-column label="模板编号" align="center" prop="templateId" />
  72 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
  73 + <template v-slot="scope">
  74 + <el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"
  75 + v-hasPermi="['system:mail-log:query']">详细</el-button>
  76 + </template>
  77 + </el-table-column>
  78 + </el-table>
  79 + <!-- 分页组件 -->
  80 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  81 + @pagination="getList"/>
  82 +
  83 + <!-- 邮件日志详细-->
  84 + <el-dialog :title="title" :visible.sync="open" width="700px" v-dialogDrag append-to-body>
  85 + <el-form ref="form" :model="form" label-width="160px">
  86 + <el-row>
  87 + <el-col :span="24">
  88 + <el-form-item label="日志主键:">{{ form.id }}</el-form-item>
  89 + <el-form-item label="用户编号:">{{ form.userId }}</el-form-item>
  90 + <el-form-item label="用户类型:">
  91 + <dict-tag :type="DICT_TYPE.USER_TYPE" :value="form.userType"/>
  92 + </el-form-item>
  93 + <el-form-item label="接收邮箱地址:">{{ form.toMail }}</el-form-item>
  94 + <el-form-item label="邮箱账号编号:">{{ form.accountId }}</el-form-item>
  95 + <el-form-item label="发送邮箱地址:">{{ form.fromMail }}</el-form-item>
  96 + <el-form-item label="模板编号:">{{ form.templateId }}</el-form-item>
  97 + <el-form-item label="模板编码:">{{ form.templateCode }}</el-form-item>
  98 + <el-form-item label="模版发送人名称:">{{ form.templateNickname }}</el-form-item>
  99 + <el-form-item label="邮件标题:">{{ form.templateTitle }}</el-form-item>
  100 + <el-form-item label="邮件内容:">
  101 + <editor v-model="form.templateContent" :min-height="192" read-only />
  102 + </el-form-item>
  103 + <el-form-item label="邮件参数:">{{ form.templateParams }}</el-form-item>
  104 + <el-form-item label="发送状态:">
  105 + <dict-tag :type="DICT_TYPE.SYSTEM_MAIL_SEND_STATUS" :value="form.sendStatus" />
  106 + </el-form-item>
  107 + <el-form-item label="发送时间:">{{ parseTime(form.sendTime) }}</el-form-item>
  108 + <el-form-item label="发送返回的消息编号:">{{ form.sendMessageId }}</el-form-item>
  109 + <el-form-item label="发送异常:">{{ form.sendException }}</el-form-item>
  110 + </el-col>
  111 + </el-row>
  112 + </el-form>
  113 + <div slot="footer" class="dialog-footer">
  114 + <el-button @click="open = false">关 闭</el-button>
  115 + </div>
  116 + </el-dialog>
  117 + </div>
  118 +</template>
  119 +
  120 +<script>
  121 +import { getMailLog, getMailLogPage } from "@/api/system/mail/log";
  122 +import Editor from '@/components/Editor';
  123 +import { getSimpleMailAccountList } from "@/api/system/mail/account";
  124 +
  125 +export default {
  126 + name: "MailLog",
  127 + components: {
  128 + Editor,
  129 + },
  130 + data() {
  131 + return {
  132 + // 遮罩层
  133 + loading: true,
  134 + // 导出遮罩层
  135 + exportLoading: false,
  136 + // 显示搜索条件
  137 + showSearch: true,
  138 + // 总条数
  139 + total: 0,
  140 + // 邮件日志列表
  141 + list: [],
  142 + // 弹出层标题
  143 + title: "",
  144 + // 是否显示弹出层
  145 + open: false,
  146 + // 查询参数
  147 + queryParams: {
  148 + pageNo: 1,
  149 + pageSize: 10,
  150 + userId: null,
  151 + userType: null,
  152 + toMail: null,
  153 + accountId: null,
  154 + templateId: null,
  155 + sendStatus: null,
  156 + sendTime: [],
  157 + },
  158 + // 表单参数
  159 + form: {},
  160 + // 邮箱账号
  161 + accountOptions: []
  162 + };
  163 + },
  164 + created() {
  165 + this.getList();
  166 + // 获得邮箱账号列表
  167 + getSimpleMailAccountList().then(response => {
  168 + this.accountOptions = response.data
  169 + })
  170 + },
  171 + methods: {
  172 + /** 查询列表 */
  173 + getList() {
  174 + this.loading = true;
  175 + // 执行查询
  176 + getMailLogPage(this.queryParams).then(response => {
  177 + this.list = response.data.list;
  178 + this.total = response.data.total;
  179 + this.loading = false;
  180 + });
  181 + },
  182 + /** 取消按钮 */
  183 + cancel() {
  184 + this.open = false;
  185 + this.reset();
  186 + },
  187 + /** 表单重置 */
  188 + reset() {
  189 + this.form = {
  190 + id: undefined,
  191 + userId: undefined,
  192 + userType: undefined,
  193 + toMail: undefined,
  194 + accountId: undefined,
  195 + fromMail: undefined,
  196 + templateId: undefined,
  197 + templateCode: undefined,
  198 + templateNickname: undefined,
  199 + templateTitle: undefined,
  200 + templateContent: undefined,
  201 + templateParams: undefined,
  202 + sendStatus: undefined,
  203 + sendTime: undefined,
  204 + sendMessageId: undefined,
  205 + sendException: undefined,
  206 + };
  207 + this.resetForm("form");
  208 + },
  209 + /** 搜索按钮操作 */
  210 + handleQuery() {
  211 + this.queryParams.pageNo = 1;
  212 + this.getList();
  213 + },
  214 + /** 重置按钮操作 */
  215 + resetQuery() {
  216 + this.resetForm("queryForm");
  217 + this.handleQuery();
  218 + },
  219 + /** 详细按钮操作 */
  220 + handleView(row) {
  221 + this.open = true;
  222 + this.form = row;
  223 + }
  224 + }
  225 +};
  226 +</script>
... ...
yudao-ui-admin/src/views/system/mail/template/index.vue 0 → 100755
  1 +<template>
  2 + <div class="app-container">
  3 + <doc-alert title="邮件配置" url="https://doc.iocoder.cn/mail" />
  4 +
  5 + <!-- 搜索工作栏 -->
  6 + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  7 + <el-form-item label="模板名称" prop="name">
  8 + <el-input v-model="queryParams.name" placeholder="请输入模板名称" clearable @keyup.enter.native="handleQuery"/>
  9 + </el-form-item>
  10 + <el-form-item label="模板编码" prop="code">
  11 + <el-input v-model="queryParams.code" placeholder="请输入模板编码" clearable @keyup.enter.native="handleQuery"/>
  12 + </el-form-item>
  13 + <el-form-item label="邮箱账号" prop="accountId">
  14 + <el-select v-model="queryParams.accountId" placeholder="请输入邮箱账号" clearable>
  15 + <el-option v-for="account in accountOptions" :key="account.id" :value="account.id" :label="account.mail" />
  16 + </el-select>
  17 + </el-form-item>
  18 + <el-form-item label="开启状态" prop="status">
  19 + <el-select v-model="queryParams.status" placeholder="请选择开启状态" clearable size="small">
  20 + <el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
  21 + :key="dict.value" :label="dict.label" :value="dict.value"/>
  22 + </el-select>
  23 + </el-form-item>
  24 + <el-form-item label="创建时间" prop="createTime">
  25 + <el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
  26 + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
  27 + </el-form-item>
  28 + <el-form-item>
  29 + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
  30 + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
  31 + </el-form-item>
  32 + </el-form>
  33 +
  34 + <!-- 操作工具栏 -->
  35 + <el-row :gutter="10" class="mb8">
  36 + <el-col :span="1.5">
  37 + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
  38 + v-hasPermi="['system:mail-template:create']">新增</el-button>
  39 + </el-col>
  40 + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  41 + </el-row>
  42 +
  43 + <!-- 列表 -->
  44 + <el-table v-loading="loading" :data="list">
  45 + <el-table-column label="模板编码" align="center" prop="code" />
  46 + <el-table-column label="模板名称" align="center" prop="name" />
  47 + <el-table-column label="模板标题" align="center" prop="title" />
  48 + <el-table-column label="模板内容" align="center" prop="content" :show-overflow-tooltip="true" />
  49 + <el-table-column label="邮箱账号" align="center" prop="accountId" width="200">
  50 + <template v-slot="scope">
  51 + {{ accountOptions.find(account => account.id === scope.row.accountId)?.mail }}
  52 + </template>
  53 + </el-table-column>
  54 + <el-table-column label="发送人名称" align="center" prop="nickname" />
  55 + <el-table-column label="开启状态" align="center" prop="status">
  56 + <template v-slot="scope">
  57 + <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
  58 + </template>
  59 + </el-table-column>
  60 + <el-table-column label="创建时间" align="center" prop="createTime" width="180">
  61 + <template v-slot="scope">
  62 + <span>{{ parseTime(scope.row.createTime) }}</span>
  63 + </template>
  64 + </el-table-column>
  65 + <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
  66 + <template v-slot="scope">
  67 + <el-button size="mini" type="text" icon="el-icon-share" @click="handleSend(scope.row)"
  68 + v-hasPermi="['system:mail-template:send-mail']">测试</el-button>
  69 + <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
  70 + v-hasPermi="['system:mail-template:update']">修改</el-button>
  71 + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  72 + v-hasPermi="['system:mail-template:delete']">删除</el-button>
  73 + </template>
  74 + </el-table-column>
  75 + </el-table>
  76 + <!-- 分页组件 -->
  77 + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
  78 + @pagination="getList"/>
  79 +
  80 + <!-- 对话框(添加 / 修改) -->
  81 + <el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
  82 + <el-form ref="form" :model="form" :rules="rules" label-width="100px">
  83 + <el-form-item label="模板名称" prop="name">
  84 + <el-input v-model="form.name" placeholder="请输入模板名称" />
  85 + </el-form-item>
  86 + <el-form-item label="模板编码" prop="code">
  87 + <el-input v-model="form.code" placeholder="请输入模板编码" />
  88 + </el-form-item>
  89 + <el-form-item label="邮箱账号" prop="accountId">
  90 + <el-select v-model="form.accountId" placeholder="请输入邮箱账号">
  91 + <el-option v-for="account in accountOptions" :key="account.id" :value="account.id" :label="account.mail" />
  92 + </el-select>
  93 + </el-form-item>
  94 + <el-form-item label="发送人名称" prop="nickname">
  95 + <el-input v-model="form.nickname" placeholder="请输入发送人名称" />
  96 + </el-form-item>
  97 + <el-form-item label="模板标题" prop="title">
  98 + <el-input v-model="form.title" placeholder="请输入模板标题" />
  99 + </el-form-item>
  100 + <el-form-item label="模板内容">
  101 + <editor v-model="form.content" :min-height="192"/>
  102 + </el-form-item>
  103 + <el-form-item label="开启状态" prop="status">
  104 + <el-radio-group v-model="form.status">
  105 + <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
  106 + :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
  107 + </el-radio-group>
  108 + </el-form-item>
  109 + <el-form-item label="备注" prop="remark">
  110 + <el-input v-model="form.remark" placeholder="请输入备注" />
  111 + </el-form-item>
  112 + </el-form>
  113 + <div slot="footer" class="dialog-footer">
  114 + <el-button type="primary" @click="submitForm">确 定</el-button>
  115 + <el-button @click="cancel">取 消</el-button>
  116 + </div>
  117 + </el-dialog>
  118 +
  119 + <!-- 对话框(发送邮件) -->
  120 + <el-dialog title="测试发送邮件" :visible.sync="sendOpen" width="500px" append-to-body>
  121 + <el-form ref="sendForm" :model="sendForm" :rules="sendRules" label-width="140px">
  122 + <el-form-item label="模板内容" prop="content">
  123 + <editor v-model="sendForm.content" :min-height="192" readonly />
  124 + </el-form-item>
  125 + <el-form-item label="收件邮箱" prop="mail">
  126 + <el-input v-model="sendForm.mail" placeholder="请输入收件邮箱" />
  127 + </el-form-item>
  128 + <el-form-item v-for="param in sendForm.params" :key="param" :label="'参数 {' + param + '}'" :prop="'templateParams.' + param">
  129 + <el-input v-model="sendForm.templateParams[param]" :placeholder="'请输入 ' + param + ' 参数'" />
  130 + </el-form-item>
  131 + </el-form>
  132 + <div slot="footer" class="dialog-footer">
  133 + <el-button type="primary" @click="submitSendForm">确 定</el-button>
  134 + <el-button @click="cancelSend">取 消</el-button>
  135 + </div>
  136 + </el-dialog>
  137 + </div>
  138 +</template>
  139 +
  140 +<script>
  141 +import { createMailTemplate, updateMailTemplate, deleteMailTemplate, getMailTemplate, getMailTemplatePage, sendMail } from "@/api/system/mail/template";
  142 +import Editor from '@/components/Editor';
  143 +import { CommonStatusEnum } from "@/utils/constants";
  144 +import { getSimpleMailAccountList } from "@/api/system/mail/account";
  145 +
  146 +export default {
  147 + name: "MailTemplate",
  148 + components: {
  149 + Editor,
  150 + },
  151 + data() {
  152 + return {
  153 + // 遮罩层
  154 + loading: true,
  155 + // 显示搜索条件
  156 + showSearch: true,
  157 + // 总条数
  158 + total: 0,
  159 + // 邮件模版列表
  160 + list: [],
  161 + // 弹出层标题
  162 + title: "",
  163 + // 是否显示弹出层
  164 + open: false,
  165 + // 查询参数
  166 + queryParams: {
  167 + pageNo: 1,
  168 + pageSize: 10,
  169 + name: null,
  170 + code: null,
  171 + accountId: null,
  172 + status: null,
  173 + createTime: [],
  174 + },
  175 + // 表单参数
  176 + form: {},
  177 + // 表单校验
  178 + rules: {
  179 + name: [{ required: true, message: "模板名称不能为空", trigger: "blur" }],
  180 + code: [{ required: true, message: "模板编码不能为空", trigger: "blur" }],
  181 + accountId: [{ required: true, message: "邮箱账号不能为空", trigger: "blur" }],
  182 + title: [{ required: true, message: "模板标题不能为空", trigger: "blur" }],
  183 + content: [{ required: true, message: "模板内容不能为空", trigger: "blur" }],
  184 + status: [{ required: true, message: "开启状态不能为空", trigger: "blur" }],
  185 + },
  186 + // 邮箱账号
  187 + accountOptions: [],
  188 +
  189 + // 发送邮箱
  190 + sendOpen: false,
  191 + sendForm: {
  192 + params: [], // 模板的参数列表
  193 + },
  194 + sendRules: {
  195 + mail: [{ required: true, message: "收件邮箱不能为空", trigger: "blur" }],
  196 + templateCode: [{ required: true, message: "模版编码不能为空", trigger: "blur" }],
  197 + templateParams: { }
  198 + }
  199 + };
  200 + },
  201 + created() {
  202 + this.getList();
  203 + // 获得邮箱账号列表
  204 + getSimpleMailAccountList().then(response => {
  205 + this.accountOptions = response.data
  206 + })
  207 + },
  208 + methods: {
  209 + /** 查询列表 */
  210 + getList() {
  211 + this.loading = true;
  212 + // 执行查询
  213 + getMailTemplatePage(this.queryParams).then(response => {
  214 + this.list = response.data.list;
  215 + this.total = response.data.total;
  216 + this.loading = false;
  217 + });
  218 + },
  219 + /** 取消按钮 */
  220 + cancel() {
  221 + this.open = false;
  222 + this.reset();
  223 + },
  224 + /** 表单重置 */
  225 + reset() {
  226 + this.form = {
  227 + id: undefined,
  228 + name: undefined,
  229 + code: undefined,
  230 + accountId: undefined,
  231 + nickname: undefined,
  232 + title: undefined,
  233 + content: undefined,
  234 + status: CommonStatusEnum.ENABLE,
  235 + remark: undefined,
  236 + };
  237 + this.resetForm("form");
  238 + },
  239 + /** 搜索按钮操作 */
  240 + handleQuery() {
  241 + this.queryParams.pageNo = 1;
  242 + this.getList();
  243 + },
  244 + /** 重置按钮操作 */
  245 + resetQuery() {
  246 + this.resetForm("queryForm");
  247 + this.handleQuery();
  248 + },
  249 + /** 新增按钮操作 */
  250 + handleAdd() {
  251 + this.reset();
  252 + this.open = true;
  253 + this.title = "添加邮件模版";
  254 + },
  255 + /** 修改按钮操作 */
  256 + handleUpdate(row) {
  257 + this.reset();
  258 + const id = row.id;
  259 + getMailTemplate(id).then(response => {
  260 + this.form = response.data;
  261 + this.open = true;
  262 + this.title = "修改邮件模版";
  263 + });
  264 + },
  265 + /** 提交按钮 */
  266 + submitForm() {
  267 + this.$refs["form"].validate(valid => {
  268 + if (!valid) {
  269 + return;
  270 + }
  271 + // 修改的提交
  272 + if (this.form.id != null) {
  273 + updateMailTemplate(this.form).then(response => {
  274 + this.$modal.msgSuccess("修改成功");
  275 + this.open = false;
  276 + this.getList();
  277 + });
  278 + return;
  279 + }
  280 + // 添加的提交
  281 + createMailTemplate(this.form).then(response => {
  282 + this.$modal.msgSuccess("新增成功");
  283 + this.open = false;
  284 + this.getList();
  285 + });
  286 + });
  287 + },
  288 + /** 删除按钮操作 */
  289 + handleDelete(row) {
  290 + const id = row.id;
  291 + this.$modal.confirm('是否确认删除邮件模版编号为"' + id + '"的数据项?').then(function() {
  292 + return deleteMailTemplate(id);
  293 + }).then(() => {
  294 + this.getList();
  295 + this.$modal.msgSuccess("删除成功");
  296 + }).catch(() => {});
  297 + },
  298 + /** 发送短息按钮 */
  299 + handleSend(row) {
  300 + this.resetSend(row);
  301 + // 设置参数
  302 + this.sendForm.content = row.content;
  303 + this.sendForm.params = row.params;
  304 + this.sendForm.templateCode = row.code;
  305 + this.sendForm.templateParams = row.params.reduce(function(obj, item) {
  306 + obj[item] = undefined;
  307 + return obj;
  308 + }, {});
  309 + // 根据 row 重置 rules
  310 + this.sendRules.templateParams = row.params.reduce(function(obj, item) {
  311 + obj[item] = { required: true, message: '参数 ' + item + " 不能为空", trigger: "change" };
  312 + return obj;
  313 + }, {});
  314 + // 设置打开
  315 + this.sendOpen = true;
  316 + },
  317 + /** 重置发送邮箱的表单 */
  318 + resetSend() {
  319 + // 根据 row 重置表单
  320 + this.sendForm = {
  321 + content: undefined,
  322 + params: undefined,
  323 + mail: undefined,
  324 + templateCode: undefined,
  325 + templateParams: {}
  326 + };
  327 + this.resetForm("sendForm");
  328 + },
  329 + /** 取消发送邮箱 */
  330 + cancelSend() {
  331 + this.sendOpen = false;
  332 + this.resetSend();
  333 + },
  334 + /** 提交按钮 */
  335 + submitSendForm() {
  336 + this.$refs["sendForm"].validate(valid => {
  337 + if (!valid) {
  338 + return;
  339 + }
  340 + // 添加的提交
  341 + sendMail(this.sendForm).then(response => {
  342 + this.$modal.msgSuccess("提交发送成功!发送结果,见发送日志编号:" + response.data);
  343 + this.sendOpen = false;
  344 + });
  345 + });
  346 + },
  347 + }
  348 +};
  349 +</script>
... ...
yudao-ui-admin/src/views/system/sms/smsLog.vue
... ... @@ -94,7 +94,7 @@
94 94 </el-table-column>
95 95 <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
96 96 <template v-slot="scope">
97   - <el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row,scope.index)"
  97 + <el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"
98 98 v-hasPermi="['system:sms-log:query']">详细</el-button>
99 99 </template>
100 100 </el-table-column>
... ...
yudao-ui-admin/src/views/system/sms/smsTemplate.vue
... ... @@ -216,7 +216,7 @@ export default {
216 216 },
217 217 sendSmsRules: {
218 218 mobile: [{ required: true, message: "手机不能为空", trigger: "blur" }],
219   - templateCode: [{ required: true, message: "手机不能为空", trigger: "blur" }],
  219 + templateCode: [{ required: true, message: "模版编码不能为空", trigger: "blur" }],
220 220 templateParams: { }
221 221 }
222 222 };
... ...
yudao-ui-admin/src/views/system/user/index.vue
... ... @@ -364,9 +364,9 @@ export default {
364 364 created() {
365 365 this.getList();
366 366 this.getTreeselect();
367   - this.getConfigKey("sys.user.init-password").then(response => {
368   - this.initPassword = response.msg;
369   - });
  367 + // this.getConfigKey("sys.user.init-password").then(response => {
  368 + // this.initPassword = response.msg;
  369 + // });
370 370 },
371 371 methods: {
372 372 // 更多操作
... ...
yudao-ui-admin/yarn.lock
... ... @@ -3999,10 +3999,10 @@ electron-to-chromium@^1.4.251:
3999 3999 resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz"
4000 4000 integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==
4001 4001  
4002   -element-ui@2.15.10:
4003   - version "2.15.10"
4004   - resolved "https://registry.npmmirror.com/element-ui/-/element-ui-2.15.10.tgz"
4005   - integrity sha512-jmD++mU2wKXbisvx4fxOl2mHaU+HWHTAq/3Wf8x9Bwyu4GdDZPLABb+CGi3DWN6fPqdgRcd74aX39DO+YHObLw==
  4002 +element-ui@2.15.12:
  4003 + version "2.15.12"
  4004 + resolved "https://registry.npmmirror.com/element-ui/-/element-ui-2.15.12.tgz#fdde927a54078b17a85541ff7c0f0dad32488b8e"
  4005 + integrity sha512-Y5FMT2BPOindU2GkDEQ5ZKUVxDawKONRNMh2eL3uBx1FOtvUJ+L6IxXLVsNxq4WnaX/UnVNgWXebl7DobygZMg==
4006 4006 dependencies:
4007 4007 async-validator "~1.8.1"
4008 4008 babel-helper-vue-jsx-merge-props "^2.0.0"
... ...