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

# Conflicts:
#	README.md
plp
xingyu 3 years ago
commit 08a4de37c2

@ -41,7 +41,20 @@
| `ruoyi-vue-pro` | Spring Boot 多模块 | **[Gitee](https://gitee.com/zhijiantianya/ruoyi-vue-pro)**     [Github](https://github.com/YunaiV/ruoyi-vue-pro) |
| `yudao-cloud` | Spring Cloud 微服务 | **[Gitee](https://gitee.com/zhijiantianya/yudao-cloud)**     [Github](https://github.com/YunaiV/yudao-cloud) |
| `Spring-Boot-Labs` | Spring Boot & Cloud 入门 | **[Gitee](https://gitee.com/zhijiantianya/SpringBoot-Labs)**     [Github](https://github.com/YunaiV/SpringBoot-Labs) |
| `ruoyi-vue-pro-mini` | 精简版:移除工作流、支付等模块 | **[Gitee](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/mini)** |
## 🐰 分支说明
| | JDK 8 完整版 | JDK 8 精简版 | JDK 17 完整版 |
|-------|-----------------------------------------------------------|--------------------------------------------------------------------|-----------------------------------------------------------------------------|
| 分支 | [`master`](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [`mini`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/mini/) | [`boot-dev`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/boot3-dev/) |
| 说明 | 包括所有功能 | 只保留核心功能 | 适配 Spring Boot 3.X |
| 系统功能 | √ | √ | √ |
| 基础设施 | √ | √ | √ |
| 会员中心 | √ | √ | √ |
| 工作流程 | √ | x | 适配中 |
| 数据报表 | √ | x | 适配中 |
| 商城系统 | √ | x | √ |
| 微信公众号 | √ | x | √ |
## 😎 开源协议
@ -55,6 +68,14 @@
③ 代码整洁、架构整洁,遵循《阿里巴巴 Java 开发手册》规范代码注释详细57000 行 Java 代码22000 行代码注释。
## 🤝 项目外包
我们也是接外包滴,如果你有项目想要外包,可以微信联系【**Aix9975**】。
团队包含专业的项目经理、架构师、前端工程师、后端工程师、测试工程师、运维工程师,可以提供全流程的外包服务。
项目可以是商城、SCRM 系统、OA 系统、物流系统、ERP 系统、CMS 系统、HIS 系统、支付系统、IM 聊天、微信公众号、微信小程序等等。
## 🐼 内置功能
系统内置多种多种业务功能,可以用于快速你的业务系统:
@ -92,6 +113,7 @@
| | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 |
| 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 |
| 🚀 | 邮件管理 | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台 |
| 🚀 | 站内信 | 系统内的消息通知,提供站内信模版、站内信消息 |
| 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
| ⭐️ | 登录日志 | 系统登录日志记录查询,包含登录异常 |
| 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 |

@ -30,7 +30,7 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties>
<revision>1.6.6-snapshot</revision>
<revision>1.7.0-snapshot</revision>
<!-- Maven 相关 -->
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>

@ -546,7 +546,7 @@ CREATE TABLE `infra_api_error_log` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 949 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
) ENGINE = InnoDB AUTO_INCREMENT = 965 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
-- ----------------------------
-- Records of infra_api_error_log
@ -584,7 +584,7 @@ CREATE TABLE `infra_codegen_column` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1582 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
) ENGINE = InnoDB AUTO_INCREMENT = 1612 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
-- ----------------------------
-- Records of infra_codegen_column
@ -616,7 +616,7 @@ CREATE TABLE `infra_codegen_table` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 122 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
) ENGINE = InnoDB AUTO_INCREMENT = 124 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
-- ----------------------------
-- Records of infra_codegen_table
@ -1191,7 +1191,7 @@ CREATE TABLE `system_dict_data` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1227 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
) ENGINE = InnoDB AUTO_INCREMENT = 1229 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
-- ----------------------------
-- Records of system_dict_data
@ -1395,6 +1395,8 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
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');
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');
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');
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 (1227, 1, '', '1', 'system_notify_template_type', 0, 'primary', '', ' - ', '1', '2023-01-28 10:35:59', '1', '2023-01-28 10:35:59', b'0');
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 (1228, 2, '', '2', 'system_notify_template_type', 0, 'success', '', ' - ', '1', '2023-01-28 10:36:20', '1', '2023-01-28 10:36:25', b'0');
COMMIT;
-- ----------------------------
@ -1415,7 +1417,7 @@ CREATE TABLE `system_dict_type` (
`deleted_time` datetime NULL DEFAULT NULL COMMENT '',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `dict_type`(`type` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 167 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
) ENGINE = InnoDB AUTO_INCREMENT = 168 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
-- ----------------------------
-- Records of system_dict_type
@ -1479,6 +1481,7 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat
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');
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');
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');
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (167, '', 'system_notify_template_type', 0, '', '1', '2023-01-28 10:35:10', '1', '2023-01-28 10:35:10', b'0', '1970-01-01 00:00:00');
COMMIT;
-- ----------------------------
@ -1527,7 +1530,7 @@ CREATE TABLE `system_login_log` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1980 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '访';
) ENGINE = InnoDB AUTO_INCREMENT = 1985 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '访';
-- ----------------------------
-- Records of system_login_log
@ -1620,7 +1623,7 @@ CREATE TABLE `system_mail_template` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
) ENGINE = InnoDB AUTO_INCREMENT = 16 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
-- ----------------------------
-- Records of system_mail_template
@ -1654,7 +1657,7 @@ CREATE TABLE `system_menu` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2144 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
) ENGINE = InnoDB AUTO_INCREMENT = 2153 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
-- ----------------------------
-- Records of system_menu
@ -1877,7 +1880,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
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');
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');
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');
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');
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-28 12:33:52', b'0');
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');
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');
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');
@ -2023,6 +2026,15 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
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');
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');
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');
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 (2144, '', '', 1, 11, 1, 'notify', 'message', NULL, 0, b'1', b'1', '1', '2023-01-28 10:25:18', '1', '2023-01-28 10:25:46', b'0');
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 (2145, '', '', 2, 0, 2144, 'notify-template', 'education', 'system/notify/template/index', 0, b'1', b'1', '', '2023-01-28 02:26:42', '1', '2023-01-28 10:27:47', b'0');
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 (2146, '', 'system:notify-template:query', 3, 1, 2145, '', '', '', 0, b'1', b'1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', b'0');
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 (2147, '', 'system:notify-template:create', 3, 2, 2145, '', '', '', 0, b'1', b'1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', b'0');
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 (2148, '', 'system:notify-template:update', 3, 3, 2145, '', '', '', 0, b'1', b'1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', b'0');
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 (2149, '', 'system:notify-template:delete', 3, 4, 2145, '', '', '', 0, b'1', b'1', '', '2023-01-28 02:26:42', '', '2023-01-28 02:26:42', b'0');
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 (2150, '', 'system:notify-template:send-notify', 3, 5, 2145, '', '', '', 0, b'1', b'1', '1', '2023-01-28 10:54:43', '1', '2023-01-28 10:54:43', b'0');
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 (2151, '', '', 2, 0, 2144, 'notify-message', 'edit', 'system/notify/message/index', 0, b'1', b'1', '', '2023-01-28 04:28:22', '1', '2023-01-28 20:59:53', b'0');
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 (2152, '', 'system:notify-message:query', 3, 1, 2151, '', '', '', 0, b'1', b'1', '', '2023-01-28 04:28:22', '', '2023-01-28 04:28:22', b'0');
COMMIT;
-- ----------------------------
@ -2053,6 +2065,72 @@ INSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creato
INSERT INTO `system_notice` (`id`, `title`, `content`, `type`, `status`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, '', '<p>123</p>', 1, 0, '110', '2022-02-22 01:01:25', '110', '2022-02-22 01:01:46', b'0', 121);
COMMIT;
-- ----------------------------
-- Table structure for system_notify_message
-- ----------------------------
DROP TABLE IF EXISTS `system_notify_message`;
CREATE TABLE `system_notify_message` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID',
`user_id` bigint NOT NULL COMMENT 'id',
`user_type` tinyint NOT NULL COMMENT '',
`template_id` bigint NOT NULL COMMENT '',
`template_code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '',
`template_nickname` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '',
`template_content` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '',
`template_type` int NOT NULL COMMENT '',
`template_params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '',
`read_status` bit(1) NOT NULL COMMENT '',
`read_time` datetime NULL DEFAULT NULL COMMENT '',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
-- ----------------------------
-- Records of system_notify_message
-- ----------------------------
BEGIN;
INSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, 1, 2, 1, 'test', '123', ' 1 2 ', 1, '{\"name\":\"1\",\"what\":\"2\"}', b'0', '2023-01-28 21:06:10', '1', '2023-01-28 11:44:08', '1', '2023-01-29 02:51:50', b'0', 1);
INSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, 1, 2, 1, 'test', '123', ' 1 2 ', 1, '{\"name\":\"1\",\"what\":\"2\"}', b'0', '2023-01-29 10:34:13', '1', '2023-01-28 11:45:04', '1', '2023-01-29 02:51:52', b'0', 1);
INSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4, 103, 2, 2, 'register', '', ' ', 2, '{\"name\":\"哈哈\"}', b'0', NULL, '1', '2023-01-28 21:02:20', '1', '2023-01-28 21:02:20', b'0', 1);
INSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5, 1, 2, 1, 'test', '123', ' ', 1, '{\"name\":\"芋艿\",\"what\":\"写代码\"}', b'0', '2023-01-29 10:41:45', '1', '2023-01-28 22:21:42', '1', '2023-01-29 02:51:54', b'0', 1);
INSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6, 1, 2, 1, 'test', '123', ' ', 1, '{\"name\":\"芋艿\",\"what\":\"写代码\"}', b'1', '2023-01-29 10:52:06', '1', '2023-01-28 22:22:07', '1', '2023-01-29 10:52:06', b'0', 1);
INSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (7, 1, 2, 1, 'test', '123', ' 2 3 ', 1, '{\"name\":\"2\",\"what\":\"3\"}', b'1', '2023-01-29 10:52:06', '1', '2023-01-28 23:45:21', '1', '2023-01-29 10:52:06', b'0', 1);
INSERT INTO `system_notify_message` (`id`, `user_id`, `user_type`, `template_id`, `template_code`, `template_nickname`, `template_content`, `template_type`, `template_params`, `read_status`, `read_time`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8, 1, 2, 2, 'register', '', ' 123 ', 2, '{\"name\":\"123\"}', b'1', '2023-01-29 10:52:06', '1', '2023-01-28 23:50:21', '1', '2023-01-29 10:52:06', b'0', 1);
COMMIT;
-- ----------------------------
-- Table structure for system_notify_template
-- ----------------------------
DROP TABLE IF EXISTS `system_notify_template`;
CREATE TABLE `system_notify_template` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '',
`name` varchar(63) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '',
`code` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '',
`nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '',
`content` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '',
`type` tinyint NOT NULL COMMENT '',
`params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '',
`status` tinyint NOT NULL COMMENT '',
`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '',
`creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '',
`updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT '' COMMENT '',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
-- ----------------------------
-- Records of system_notify_template
-- ----------------------------
BEGIN;
COMMIT;
-- ----------------------------
-- Table structure for system_oauth2_access_token
-- ----------------------------
@ -2073,7 +2151,7 @@ CREATE TABLE `system_oauth2_access_token` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1214 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访';
) ENGINE = InnoDB AUTO_INCREMENT = 1249 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访';
-- ----------------------------
-- Records of system_oauth2_access_token
@ -2195,7 +2273,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 588 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 ';
) ENGINE = InnoDB AUTO_INCREMENT = 593 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 ';
-- ----------------------------
-- Records of system_oauth2_refresh_token
@ -2235,7 +2313,7 @@ CREATE TABLE `system_operate_log` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '',
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4177 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
) ENGINE = InnoDB AUTO_INCREMENT = 4245 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '';
-- ----------------------------
-- Records of system_operate_log
@ -3353,7 +3431,7 @@ CREATE TABLE `system_users` (
-- Records of system_users
-- ----------------------------
BEGIN;
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);
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-29 10:05:18', 'admin', '2021-01-05 17:03:47', NULL, '2023-01-29 10:05:18', b'0', 1);
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);
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);
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);

@ -14,7 +14,7 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties>
<revision>1.6.6-snapshot</revision>
<revision>1.7.0-snapshot</revision>
<!-- 统一依赖管理 -->
<spring.boot.version>2.7.7</spring.boot.version>
<!-- Web 相关 -->

@ -40,6 +40,10 @@ public class BannerApplicationRunner implements ApplicationRunner {
if (isNotPresent("cn.iocoder.yudao.framework.flowable.config.YudaoFlowableConfiguration")) {
System.out.println("[工作流模块 yudao-module-bpm - 已禁用][参考 https://doc.iocoder.cn/bpm/ 开启]");
}
// 微信公众号
if (isNotPresent("cn.iocoder.yudao.module.mp.framework.mp.config.MpConfiguration")) {
System.out.println("[微信公众号 yudao-module-mp - 已禁用][参考 https://doc.iocoder.cn/mp/build/ 开启]");
}
});
}

@ -1,7 +1,9 @@
package cn.iocoder.yudao.framework.mybatis.core.query;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.mybatis.core.enums.SqlConstants;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;

@ -146,19 +146,19 @@ public class QueryWrapperX<T> extends QueryWrapper<T> {
*
* @return this
*/
public QueryWrapperX<T> limit1() {
public QueryWrapperX<T> limitN(int n) {
Assert.notNull(SqlConstants.DB_TYPE, "获取不到数据库的类型");
switch (SqlConstants.DB_TYPE) {
case ORACLE:
case ORACLE_12C:
super.eq("ROWNUM", 1);
super.eq("ROWNUM", n);
break;
case SQL_SERVER:
case SQL_SERVER2005:
super.select("TOP 1 *"); // 由于 SQL Server 是通过 SELECT TOP 1 实现限制一条,所以只好使用 * 查询剩余字段
super.select("TOP " + n + " *"); // 由于 SQL Server 是通过 SELECT TOP 1 实现限制一条,所以只好使用 * 查询剩余字段
break;
default:
super.last("LIMIT 1");
super.last("LIMIT " + n);
}
return this;
}

@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.system.api.notify;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import javax.validation.Valid;
/**
* API
*
* @author xrcoder
*/
public interface NotifyMessageSendApi {
/**
* Admin
*
* @param reqDTO
* @return ID
*/
Long sendSingleMessageToAdmin(@Valid NotifySendSingleToUserReqDTO reqDTO);
/**
* Member
*
* @param reqDTO
* @return ID
*/
Long sendSingleMessageToMember(@Valid NotifySendSingleToUserReqDTO reqDTO);
}

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.system.api.notify.dto;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Map;
/**
* Admin Member
*
* @author xrcoder
*/
@Data
public class NotifySendSingleToUserReqDTO {
/**
*
*/
@NotNull(message = "用户编号不能为空")
private Long userId;
/**
*
*/
@NotEmpty(message = "站内信模板编号不能为空")
private String templateCode;
/**
*
*/
private Map<String, Object> templateParams;
}

@ -153,4 +153,13 @@ public interface ErrorCodeConstants {
ErrorCode MAIL_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1002025000, "模板参数({})缺失");
ErrorCode MAIL_SEND_MAIL_NOT_EXISTS = new ErrorCode(1002025000, "邮箱不存在");
// ========== 站内信模版 1002026000 ==========
ErrorCode NOTIFY_TEMPLATE_NOT_EXISTS = new ErrorCode(1002026000, "站内信模版不存在");
ErrorCode NOTIFY_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1002026001, "已经存在编码为【{}】的站内信模板");
// ========== 站内信模版 1002027000 ==========
// ========== 站内信发送 1002028000 ==========
ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1002025000, "模板参数({})缺失");
}

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.system.api.notify;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.service.notify.NotifyMessageService;
import cn.iocoder.yudao.module.system.service.notify.NotifySendService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* API
*
* @author xrcoder
*/
@Service
public class NotifyMessageSendApiImpl implements NotifyMessageSendApi {
@Resource
private NotifySendService notifySendService;
@Override
public Long sendSingleMessageToAdmin(NotifySendSingleToUserReqDTO reqDTO) {
return notifySendService.sendSingleNotifyToAdmin(reqDTO.getUserId(),
reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
}
@Override
public Long sendSingleMessageToMember(NotifySendSingleToUserReqDTO reqDTO) {
return notifySendService.sendSingleNotifyToMember(reqDTO.getUserId(),
reqDTO.getTemplateCode(), reqDTO.getTemplateParams());
}
}

@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.system.controller.admin.notify;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageRespVO;
import cn.iocoder.yudao.module.system.convert.notify.NotifyMessageConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import cn.iocoder.yudao.module.system.service.notify.NotifyMessageService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Api(tags = "管理后台 - 我的站内信")
@RestController
@RequestMapping("/system/notify-message")
@Validated
public class NotifyMessageController {
@Resource
private NotifyMessageService notifyMessageService;
// ========== 管理所有的站内信 ==========
@GetMapping("/get")
@ApiOperation("获得站内信")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('system:notify-message:query')")
public CommonResult<NotifyMessageRespVO> getNotifyMessage(@RequestParam("id") Long id) {
NotifyMessageDO notifyMessage = notifyMessageService.getNotifyMessage(id);
return success(NotifyMessageConvert.INSTANCE.convert(notifyMessage));
}
@GetMapping("/page")
@ApiOperation("获得站内信分页")
@PreAuthorize("@ss.hasPermission('system:notify-message:query')")
public CommonResult<PageResult<NotifyMessageRespVO>> getNotifyMessagePage(@Valid NotifyMessagePageReqVO pageVO) {
PageResult<NotifyMessageDO> pageResult = notifyMessageService.getNotifyMessagePage(pageVO);
return success(NotifyMessageConvert.INSTANCE.convertPage(pageResult));
}
// ========== 查看自己的站内信 ==========
@GetMapping("/my-page")
@ApiOperation("获得我的站内信分页")
public CommonResult<PageResult<NotifyMessageRespVO>> getMyMyNotifyMessagePage(@Valid NotifyMessageMyPageReqVO pageVO) {
PageResult<NotifyMessageDO> pageResult = notifyMessageService.getMyMyNotifyMessagePage(pageVO,
getLoginUserId(), UserTypeEnum.ADMIN.getValue());
return success(NotifyMessageConvert.INSTANCE.convertPage(pageResult));
}
@PutMapping("/update-read")
@ApiOperation("标记站内信为已读")
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
public CommonResult<Boolean> updateNotifyMessageRead(@RequestParam("ids") List<Long> ids) {
notifyMessageService.updateNotifyMessageRead(ids, getLoginUserId(), UserTypeEnum.ADMIN.getValue());
return success(Boolean.TRUE);
}
@PutMapping("/update-all-read")
@ApiOperation("标记所有站内信为已读")
public CommonResult<Boolean> updateAllNotifyMessageRead() {
notifyMessageService.updateAllNotifyMessageRead(getLoginUserId(), UserTypeEnum.ADMIN.getValue());
return success(Boolean.TRUE);
}
@GetMapping("/get-unread-list")
@ApiOperation("获取当前用户的最新站内信列表,默认 10 条")
@ApiImplicitParam(name = "size", value = "10", defaultValue = "10", dataTypeClass = Integer.class)
public CommonResult<List<NotifyMessageRespVO>> getUnreadNotifyMessageList(
@RequestParam(name = "size", defaultValue = "10") Integer size) {
List<NotifyMessageDO> list = notifyMessageService.getUnreadNotifyMessageList(
getLoginUserId(), UserTypeEnum.ADMIN.getValue(), size);
return success(NotifyMessageConvert.INSTANCE.convertList(list));
}
@GetMapping("/get-unread-count")
@ApiOperation("获得当前用户的未读站内信数量")
public CommonResult<Long> getUnreadNotifyMessageCount() {
return success(notifyMessageService.getUnreadNotifyMessageCount(getLoginUserId(), UserTypeEnum.ADMIN.getValue()));
}
}

@ -0,0 +1,83 @@
package cn.iocoder.yudao.module.system.controller.admin.notify;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.*;
import cn.iocoder.yudao.module.system.convert.notify.NotifyTemplateConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.service.notify.NotifySendService;
import cn.iocoder.yudao.module.system.service.notify.NotifyTemplateService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Api(tags = "管理后台 - 站内信模版")
@RestController
@RequestMapping("/system/notify-template")
@Validated
public class NotifyTemplateController {
@Resource
private NotifyTemplateService notifyTemplateService;
@Resource
private NotifySendService notifySendService;
@PostMapping("/create")
@ApiOperation("创建站内信模版")
@PreAuthorize("@ss.hasPermission('system:notify-template:create')")
public CommonResult<Long> createNotifyTemplate(@Valid @RequestBody NotifyTemplateCreateReqVO createReqVO) {
return success(notifyTemplateService.createNotifyTemplate(createReqVO));
}
@PutMapping("/update")
@ApiOperation("更新站内信模版")
@PreAuthorize("@ss.hasPermission('system:notify-template:update')")
public CommonResult<Boolean> updateNotifyTemplate(@Valid @RequestBody NotifyTemplateUpdateReqVO updateReqVO) {
notifyTemplateService.updateNotifyTemplate(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@ApiOperation("删除站内信模版")
@ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('system:notify-template:delete')")
public CommonResult<Boolean> deleteNotifyTemplate(@RequestParam("id") Long id) {
notifyTemplateService.deleteNotifyTemplate(id);
return success(true);
}
@GetMapping("/get")
@ApiOperation("获得站内信模版")
@ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
@PreAuthorize("@ss.hasPermission('system:notify-template:query')")
public CommonResult<NotifyTemplateRespVO> getNotifyTemplate(@RequestParam("id") Long id) {
NotifyTemplateDO notifyTemplate = notifyTemplateService.getNotifyTemplate(id);
return success(NotifyTemplateConvert.INSTANCE.convert(notifyTemplate));
}
@GetMapping("/page")
@ApiOperation("获得站内信模版分页")
@PreAuthorize("@ss.hasPermission('system:notify-template:query')")
public CommonResult<PageResult<NotifyTemplateRespVO>> getNotifyTemplatePage(@Valid NotifyTemplatePageReqVO pageVO) {
PageResult<NotifyTemplateDO> pageResult = notifyTemplateService.getNotifyTemplatePage(pageVO);
return success(NotifyTemplateConvert.INSTANCE.convertPage(pageResult));
}
@PostMapping("/send-notify")
@ApiOperation("发送站内信")
@PreAuthorize("@ss.hasPermission('system:notify-template:send-notify')")
public CommonResult<Long> sendNotify(@Valid @RequestBody NotifyTemplateSendReqVO sendReqVO) {
return success(notifySendService.sendSingleNotifyToAdmin(sendReqVO.getUserId(),
sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams()));
}
}

@ -0,0 +1,61 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
/**
* Base VO VO 使
* VO Swagger
*/
@Data
public class NotifyMessageBaseVO {
@ApiModelProperty(value = "用户编号", required = true, example = "25025")
@NotNull(message = "用户编号不能为空")
private Long userId;
@ApiModelProperty(value = "用户类型", required = true, example = "1", notes = "参见 UserTypeEnum 枚举")
@NotNull(message = "用户类型不能为空")
private Byte userType;
@ApiModelProperty(value = "模版编号", required = true, example = "13013")
@NotNull(message = "模版编号不能为空")
private Long templateId;
@ApiModelProperty(value = "模板编码", required = true, example = "test_01")
@NotNull(message = "模板编码不能为空")
private String templateCode;
@ApiModelProperty(value = "模版发送人名称", required = true, example = "芋艿")
@NotNull(message = "模版发送人名称不能为空")
private String templateNickname;
@ApiModelProperty(value = "模版内容", required = true, example = "测试内容")
@NotNull(message = "模版内容不能为空")
private String templateContent;
@ApiModelProperty(value = "模版类型", required = true, example = "2")
@NotNull(message = "模版类型不能为空")
private Integer templateType;
@ApiModelProperty(value = "模版参数", required = true)
@NotNull(message = "模版参数不能为空")
private Map<String, Object> templateParams;
@ApiModelProperty(value = "是否已读", required = true, example = "true")
@NotNull(message = "是否已读不能为空")
private Boolean readStatus;
@ApiModelProperty(value = "阅读时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime readTime;
}

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("管理后台 - 站内信分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyMessageMyPageReqVO extends PageParam {
@ApiModelProperty(value = "是否已读", example = "true")
private Boolean readStatus;
@ApiModelProperty(value = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

@ -0,0 +1,38 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import java.util.Date;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("管理后台 - 站内信分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyMessagePageReqVO extends PageParam {
@ApiModelProperty(value = "用户编号", example = "25025")
private Long userId;
@ApiModelProperty(value = "用户类型", example = "1")
private Integer userType;
@ApiModelProperty(value = "模板编码", example = "test_01")
private String templateCode;
@ApiModelProperty(value = "模版类型", example = "2")
private Integer templateType;
@ApiModelProperty(value = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
@ApiModel("管理后台 - 站内信 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyMessageRespVO extends NotifyMessageBaseVO {
@ApiModelProperty(value = "ID", required = true, example = "1024")
private Long id;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* Base VO VO 使
* VO Swagger
*/
@Data
public class NotifyTemplateBaseVO {
@ApiModelProperty(value = "模版名称", required = true, example = "测试模版")
@NotEmpty(message = "模版名称不能为空")
private String name;
@ApiModelProperty(value = "模版编码", required = true, example = "SEND_TEST")
@NotNull(message = "模版编码不能为空")
private String code;
@ApiModelProperty(value = "模版类型", required = true, example = "1", notes = "对应 system_notify_template_type 字典")
@NotNull(message = "模版类型不能为空")
private Integer type;
@ApiModelProperty(value = "发送人名称", required = true, example = "土豆")
@NotEmpty(message = "发送人名称不能为空")
private String nickname;
@ApiModelProperty(value = "模版内容", required = true, example = "我是模版内容")
@NotEmpty(message = "模版内容不能为空")
private String content;
@ApiModelProperty(value = "状态", required = true, example = "1", notes = "参见 CommonStatusEnum 枚举")
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
private Integer status;
@ApiModelProperty(value = "备注", example = "我是备注")
private String remark;
}

@ -0,0 +1,11 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import lombok.*;
import io.swagger.annotations.*;
@ApiModel("管理后台 - 站内信模版创建 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyTemplateCreateReqVO extends NotifyTemplateBaseVO {
}

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import lombok.*;
import java.time.LocalDateTime;
import java.util.*;
import io.swagger.annotations.*;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ApiModel("管理后台 - 站内信模版分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyTemplatePageReqVO extends PageParam {
@ApiModelProperty(value = "模版编码", example = "test_01")
private String code;
@ApiModelProperty(value = "模版名称", example = "我是名称")
private String name;
@ApiModelProperty(value = "状态", example = "1", notes = "参见 CommonStatusEnum 枚举类")
private Integer status;
@ApiModelProperty(value = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import lombok.*;
import java.util.*;
import io.swagger.annotations.*;
@ApiModel("管理后台 - 站内信模版 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyTemplateRespVO extends NotifyTemplateBaseVO {
@ApiModelProperty(value = "ID", required = true, example = "1024")
private Long id;
@ApiModelProperty(value = "参数数组", example = "name,code")
private List<String> params;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.Map;
@ApiModel("管理后台 - 站内信模板的发送 Request VO")
@Data
public class NotifyTemplateSendReqVO {
@ApiModelProperty(value = "用户id", required = true, example = "01")
@NotNull(message = "用户id不能为空")
private Long userId;
@ApiModelProperty(value = "模板编码", required = true, example = "01")
@NotEmpty(message = "模板编码不能为空")
private String templateCode;
@ApiModelProperty(value = "模板参数")
private Map<String, Object> templateParams;
}

@ -0,0 +1,17 @@
package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template;
import lombok.*;
import io.swagger.annotations.*;
import javax.validation.constraints.*;
@ApiModel("管理后台 - 站内信模版更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class NotifyTemplateUpdateReqVO extends NotifyTemplateBaseVO {
@ApiModelProperty(value = "ID", required = true, example = "1024")
@NotNull(message = "ID 不能为空")
private Long id;
}

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.system.convert.notify;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageRespVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* Convert
*
* @author xrcoder
*/
@Mapper
public interface NotifyMessageConvert {
NotifyMessageConvert INSTANCE = Mappers.getMapper(NotifyMessageConvert.class);
NotifyMessageRespVO convert(NotifyMessageDO bean);
List<NotifyMessageRespVO> convertList(List<NotifyMessageDO> list);
PageResult<NotifyMessageRespVO> convertPage(PageResult<NotifyMessageDO> page);
}

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.system.convert.notify;
import java.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateRespVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
/**
* Convert
*
* @author xrcoder
*/
@Mapper
public interface NotifyTemplateConvert {
NotifyTemplateConvert INSTANCE = Mappers.getMapper(NotifyTemplateConvert.class);
NotifyTemplateDO convert(NotifyTemplateCreateReqVO bean);
NotifyTemplateDO convert(NotifyTemplateUpdateReqVO bean);
NotifyTemplateRespVO convert(NotifyTemplateDO bean);
List<NotifyTemplateRespVO> convertList(List<NotifyTemplateDO> list);
PageResult<NotifyTemplateRespVO> convertPage(PageResult<NotifyTemplateDO> page);
}

@ -0,0 +1,101 @@
package cn.iocoder.yudao.module.system.dal.dataobject.notify;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.*;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.Map;
/**
* DO
*
* @author xrcoder
*/
@TableName(value = "system_notify_message", autoResultMap = true)
@KeySequence("system_notify_message_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NotifyMessageDO extends BaseDO {
/**
*
*/
@TableId
private Long id;
/**
*
*
* MemberUserDO id AdminUserDO id
*/
private Long userId;
/**
*
*
* {@link UserTypeEnum}
*/
private Integer userType;
// ========= 模板相关字段 =========
/**
*
*
* {@link NotifyTemplateDO#getId()}
*/
private Long templateId;
/**
*
*
* {@link NotifyTemplateDO#getCode()}
*/
private String templateCode;
/**
*
*
* {@link NotifyTemplateDO#getType()}
*/
private Integer templateType;
/**
*
*
* {@link NotifyTemplateDO#getNickname()}
*/
private String templateNickname;
/**
*
*
* {@link NotifyTemplateDO#getContent()}
*/
private String templateContent;
/**
*
*
* {@link NotifyTemplateDO#getParams()}
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private Map<String, Object> templateParams;
// ========= 读取相关字段 =========
/**
*
*/
private Boolean readStatus;
/**
*
*/
private LocalDateTime readTime;
}

@ -0,0 +1,72 @@
package cn.iocoder.yudao.module.system.dal.dataobject.notify;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import lombok.*;
import java.util.List;
/**
* DO
*
* @author xrcoder
*/
@TableName(value = "system_notify_template", autoResultMap = true)
@KeySequence("system_notify_template_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NotifyTemplateDO extends BaseDO {
/**
* ID
*/
@TableId
private Long id;
/**
*
*/
private String name;
/**
*
*/
private String code;
/**
*
*
* system_notify_template_type
*/
private Integer type;
/**
*
*/
private String nickname;
/**
*
*/
private String content;
/**
*
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private List<String> params;
/**
*
*
* {@link CommonStatusEnum}
*/
private Integer status;
/**
*
*/
private String remark;
}

@ -0,0 +1,70 @@
package cn.iocoder.yudao.module.system.dal.mysql.notify;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
@Mapper
public interface NotifyMessageMapper extends BaseMapperX<NotifyMessageDO> {
default PageResult<NotifyMessageDO> selectPage(NotifyMessagePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<NotifyMessageDO>()
.eqIfPresent(NotifyMessageDO::getUserId, reqVO.getUserId())
.eqIfPresent(NotifyMessageDO::getUserType, reqVO.getUserType())
.likeIfPresent(NotifyMessageDO::getTemplateCode, reqVO.getTemplateCode())
.eqIfPresent(NotifyMessageDO::getTemplateType, reqVO.getTemplateType())
.betweenIfPresent(NotifyMessageDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(NotifyMessageDO::getId));
}
default PageResult<NotifyMessageDO> selectPage(NotifyMessageMyPageReqVO reqVO, Long userId, Integer userType) {
return selectPage(reqVO, new LambdaQueryWrapperX<NotifyMessageDO>()
.eqIfPresent(NotifyMessageDO::getReadStatus, reqVO.getReadStatus())
.betweenIfPresent(NotifyMessageDO::getCreateTime, reqVO.getCreateTime())
.eq(NotifyMessageDO::getUserId, userId)
.eq(NotifyMessageDO::getUserType, userType)
.orderByDesc(NotifyMessageDO::getId));
}
default int updateListRead(Collection<Long> ids, Long userId, Integer userType) {
return update(new NotifyMessageDO().setReadStatus(true).setReadTime(LocalDateTime.now()),
new LambdaQueryWrapperX<NotifyMessageDO>()
.in(NotifyMessageDO::getId, ids)
.eq(NotifyMessageDO::getUserId, userId)
.eq(NotifyMessageDO::getUserType, userType)
.eq(NotifyMessageDO::getReadStatus, false));
}
default int updateListRead(Long userId, Integer userType) {
return update(new NotifyMessageDO().setReadStatus(true).setReadTime(LocalDateTime.now()),
new LambdaQueryWrapperX<NotifyMessageDO>()
.eq(NotifyMessageDO::getUserId, userId)
.eq(NotifyMessageDO::getUserType, userType)
.eq(NotifyMessageDO::getReadStatus, false));
}
default List<NotifyMessageDO> selectUnreadListByUserIdAndUserType(Long userId, Integer userType, Integer size) {
return selectList(new QueryWrapperX<NotifyMessageDO>() // 由于要使用 limitN 语句,所以只能用 QueryWrapperX
.eq("user_id", userId)
.eq("user_type", userType)
.eq("read_status", false)
.orderByDesc("id").limitN(size));
}
default Long selectUnreadCountByUserIdAndUserType(Long userId, Integer userType) {
return selectCount(new LambdaQueryWrapperX<NotifyMessageDO>()
.eq(NotifyMessageDO::getReadStatus, false)
.eq(NotifyMessageDO::getUserId, userId)
.eq(NotifyMessageDO::getUserType, userType));
}
}

@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.system.dal.mysql.notify;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface NotifyTemplateMapper extends BaseMapperX<NotifyTemplateDO> {
default NotifyTemplateDO selectByCode(String code) {
return selectOne(NotifyTemplateDO::getCode, code);
}
default PageResult<NotifyTemplateDO> selectPage(NotifyTemplatePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<NotifyTemplateDO>()
.likeIfPresent(NotifyTemplateDO::getCode, reqVO.getCode())
.likeIfPresent(NotifyTemplateDO::getName, reqVO.getName())
.eqIfPresent(NotifyTemplateDO::getStatus, reqVO.getStatus())
.betweenIfPresent(NotifyTemplateDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(NotifyTemplateDO::getId));
}
}

@ -22,7 +22,7 @@ public interface SmsCodeMapper extends BaseMapperX<SmsCodeDO> {
.eqIfPresent("scene", scene)
.eqIfPresent("code", code)
.orderByDesc("id")
.limit1());
.limitN(1));
}
}

@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.system.mq.consumer.notify;
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessageListener;
import cn.iocoder.yudao.module.system.mq.message.notify.NotifyTemplateRefreshMessage;
import cn.iocoder.yudao.module.system.service.notify.NotifyTemplateService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* {@link NotifyTemplateRefreshMessage}
*
* @author xrcoder
*/
@Component
@Slf4j
public class NotifyTemplateRefreshConsumer extends AbstractChannelMessageListener<NotifyTemplateRefreshMessage> {
@Resource
private NotifyTemplateService notifyTemplateService;
@Override
public void onMessage(NotifyTemplateRefreshMessage message) {
log.info("[onMessage][收到 NotifyTemplate 刷新消息]");
notifyTemplateService.initLocalCache();
}
}

@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.system.mq.message.notify;
import cn.iocoder.yudao.framework.mq.core.pubsub.AbstractChannelMessage;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* Message
*
* @author xrcoder
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class NotifyTemplateRefreshMessage extends AbstractChannelMessage {
@Override
public String getChannel() {
return "system.notify-template.refresh";
}
}

@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.system.mq.producer.notify;
import cn.iocoder.yudao.framework.mq.core.RedisMQTemplate;
import cn.iocoder.yudao.module.system.mq.message.notify.NotifyTemplateRefreshMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* Notify Producer
*
* @author xrcoder
* @since 2022-08-06
*/
@Slf4j
@Component
public class NotifyProducer {
@Resource
private RedisMQTemplate redisMQTemplate;
/**
* {@link NotifyTemplateRefreshMessage}
*/
public void sendNotifyTemplateRefreshMessage() {
NotifyTemplateRefreshMessage message = new NotifyTemplateRefreshMessage();
redisMQTemplate.send(message);
}
}

@ -88,8 +88,7 @@ public class MailSendServiceImpl implements MailSendService {
// 校验邮箱是否存在
mail = checkMail(mail);
// 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志
List<KeyValue<String, Object>> newTemplateParams = buildTemplateParams(template, templateParams);
checkTemplateParams(template, templateParams);
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus());
@ -152,21 +151,19 @@ public class MailSendServiceImpl implements MailSendService {
}
/**
* KeyValue
*
*
* @param template
* @param templateParams
* @return
* @param templateParams
*/
@VisibleForTesting
public List<KeyValue<String, Object>> buildTemplateParams(MailTemplateDO template, Map<String, Object> templateParams) {
return template.getParams().stream().map(key -> {
public void checkTemplateParams(MailTemplateDO template, Map<String, Object> templateParams) {
template.getParams().forEach(key -> {
Object value = templateParams.get(key);
if (value == null) {
throw exception(MAIL_SEND_TEMPLATE_PARAM_MISS, key);
}
return new KeyValue<>(key, value);
}).collect(Collectors.toList());
});
}
}

@ -0,0 +1,97 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Service
*
* @author xrcoder
*/
public interface NotifyMessageService {
/**
*
*
* @param userId
* @param userType
* @param template
* @param templateContent
* @param templateParams
* @return
*/
Long createNotifyMessage(Long userId, Integer userType,
NotifyTemplateDO template, String templateContent, Map<String, Object> templateParams);
/**
*
*
* @param pageReqVO
* @return
*/
PageResult<NotifyMessageDO> getNotifyMessagePage(NotifyMessagePageReqVO pageReqVO);
/**
*
*
* @param pageReqVO
* @param userId
* @param userType
* @return
*/
PageResult<NotifyMessageDO> getMyMyNotifyMessagePage(NotifyMessageMyPageReqVO pageReqVO, Long userId, Integer userType);
/**
*
*
* @param id
* @return
*/
NotifyMessageDO getNotifyMessage(Long id);
/**
*
*
* @param userId
* @param userType
* @param size
* @return
*/
List<NotifyMessageDO> getUnreadNotifyMessageList(Long userId, Integer userType, Integer size);
/**
*
*
* @param userId
* @param userType
* @return
*/
Long getUnreadNotifyMessageCount(Long userId, Integer userType);
/**
*
*
* @param ids
* @param userId
* @param userType
* @return
*/
int updateNotifyMessageRead(Collection<Long> ids, Long userId, Integer userType);
/**
*
*
* @param userId
* @param userType
* @return
*/
int updateAllNotifyMessageRead(Long userId, Integer userType);
}

@ -0,0 +1,77 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
/**
* Service
*
* @author xrcoder
*/
@Service
@Validated
public class NotifyMessageServiceImpl implements NotifyMessageService {
@Resource
private NotifyMessageMapper notifyMessageMapper;
@Override
public Long createNotifyMessage(Long userId, Integer userType,
NotifyTemplateDO template, String templateContent, Map<String, Object> templateParams) {
NotifyMessageDO message = new NotifyMessageDO().setUserId(userId).setUserType(userType)
.setTemplateId(template.getId()).setTemplateCode(template.getCode())
.setTemplateType(template.getType()).setTemplateNickname(template.getNickname())
.setTemplateContent(templateContent).setTemplateParams(templateParams).setReadStatus(false);
notifyMessageMapper.insert(message);
return message.getId();
}
@Override
public PageResult<NotifyMessageDO> getNotifyMessagePage(NotifyMessagePageReqVO pageReqVO) {
return notifyMessageMapper.selectPage(pageReqVO);
}
@Override
public PageResult<NotifyMessageDO> getMyMyNotifyMessagePage(NotifyMessageMyPageReqVO pageReqVO, Long userId, Integer userType) {
return notifyMessageMapper.selectPage(pageReqVO, userId, userType);
}
@Override
public NotifyMessageDO getNotifyMessage(Long id) {
return notifyMessageMapper.selectById(id);
}
@Override
public List<NotifyMessageDO> getUnreadNotifyMessageList(Long userId, Integer userType, Integer size) {
return notifyMessageMapper.selectUnreadListByUserIdAndUserType(userId, userType, size);
}
@Override
public Long getUnreadNotifyMessageCount(Long userId, Integer userType) {
return notifyMessageMapper.selectUnreadCountByUserIdAndUserType(userId, userType);
}
@Override
public int updateNotifyMessageRead(Collection<Long> ids, Long userId, Integer userType) {
return notifyMessageMapper.updateListRead(ids, userId, userType);
}
@Override
public int updateAllNotifyMessageRead(Long userId, Integer userType) {
return notifyMessageMapper.updateListRead(userId, userType);
}
}

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.system.service.notify;
import java.util.List;
import java.util.Map;
/**
* Service
*
* @author xrcoder
*/
public interface NotifySendService {
/**
*
*
* mobile 使 userId
*
* @param userId
* @param templateCode
* @param templateParams
* @return
*/
Long sendSingleNotifyToAdmin(Long userId,
String templateCode, Map<String, Object> templateParams);
/**
* APP
*
* mobile 使 userId
*
* @param userId
* @param templateCode
* @param templateParams
* @return
*/
Long sendSingleNotifyToMember(Long userId,
String templateCode, Map<String, Object> templateParams);
/**
*
*
* @param userId
* @param userType
* @param templateCode
* @param templateParams
* @return
*/
Long sendSingleNotify( Long userId, Integer userType,
String templateCode, Map<String, Object> templateParams);
default void sendBatchNotify(List<String> mobiles, List<Long> userIds, Integer userType,
String templateCode, Map<String, Object> templateParams) {
throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!");
}
}

@ -0,0 +1,86 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
/**
* Service
*
* @author xrcoder
*/
@Service
@Validated
@Slf4j
public class NotifySendServiceImpl implements NotifySendService {
@Resource
private NotifyTemplateService notifyTemplateService;
@Resource
private NotifyMessageService notifyMessageService;
@Override
public Long sendSingleNotifyToAdmin(Long userId, String templateCode, Map<String, Object> templateParams) {
return sendSingleNotify(userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams);
}
@Override
public Long sendSingleNotifyToMember(Long userId, String templateCode, Map<String, Object> templateParams) {
return sendSingleNotify(userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams);
}
@Override
public Long sendSingleNotify(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams) {
// 校验模版
NotifyTemplateDO template = checkNotifyTemplateValid(templateCode);
if (Objects.equals(template.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
log.info("[sendSingleNotify][模版({})已经关闭,无法给用户({}/{})发送]", templateCode, userId, userType);
return null;
}
// 校验参数
checkTemplateParams(template, templateParams);
// 发送站内信
String content = notifyTemplateService.formatNotifyTemplateContent(template.getContent(), templateParams);
return notifyMessageService.createNotifyMessage(userId, userType, template, content, templateParams);
}
@VisibleForTesting
public NotifyTemplateDO checkNotifyTemplateValid(String templateCode) {
// 获得站内信模板。考虑到效率,从缓存中获取
NotifyTemplateDO template = notifyTemplateService.getNotifyTemplateByCodeFromCache(templateCode);
// 站内信模板不存在
if (template == null) {
throw exception(NOTICE_NOT_FOUND);
}
return template;
}
/**
*
*
* @param template
* @param templateParams
*/
@VisibleForTesting
public void checkTemplateParams(NotifyTemplateDO template, Map<String, Object> templateParams) {
template.getParams().forEach(key -> {
Object value = templateParams.get(key);
if (value == null) {
throw exception(NOTIFY_SEND_TEMPLATE_PARAM_MISS, key);
}
});
}
}

@ -0,0 +1,79 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import javax.validation.Valid;
import java.util.Map;
/**
* Service
*
* @author xrcoder
*/
public interface NotifyTemplateService {
/**
*
*/
void initLocalCache();
/**
*
*
* @param code
* @return
*/
NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code);
/**
*
*
* @param createReqVO
* @return
*/
Long createNotifyTemplate(@Valid NotifyTemplateCreateReqVO createReqVO);
/**
*
*
* @param updateReqVO
*/
void updateNotifyTemplate(@Valid NotifyTemplateUpdateReqVO updateReqVO);
/**
*
*
* @param id
*/
void deleteNotifyTemplate(Long id);
/**
*
*
* @param id
* @return
*/
NotifyTemplateDO getNotifyTemplate(Long id);
/**
*
*
* @param pageReqVO
* @return
*/
PageResult<NotifyTemplateDO> getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO);
/**
*
*
* @param content
* @param params
* @return
*/
String formatNotifyTemplateContent(String content, Map<String, Object> params);
}

@ -0,0 +1,164 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
import cn.iocoder.yudao.module.system.convert.notify.NotifyTemplateConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper;
import cn.iocoder.yudao.module.system.mq.producer.notify.NotifyProducer;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
/**
* Service
*
* @author xrcoder
*/
@Service
@Validated
@Slf4j
public class NotifyTemplateServiceImpl implements NotifyTemplateService {
/**
* {}
*/
private static final Pattern PATTERN_PARAMS = Pattern.compile("\\{(.*?)}");
@Resource
private NotifyTemplateMapper notifyTemplateMapper;
@Resource
private NotifyProducer notifyProducer;
/**
*
* key {@link NotifyTemplateDO#getCode()}
* <p>
* volatile
*/
private volatile Map<String, NotifyTemplateDO> notifyTemplateCache;
/**
*
*/
@Override
@PostConstruct
public void initLocalCache() {
// 第一步:查询数据
List<NotifyTemplateDO> templates = notifyTemplateMapper.selectList();
log.info("[initLocalCache][缓存站内信模版,数量为:{}]", templates.size());
// 第二步:构建缓存
notifyTemplateCache = CollectionUtils.convertMap(templates, NotifyTemplateDO::getCode);
}
@Override
public NotifyTemplateDO getNotifyTemplateByCodeFromCache(String code) {
return notifyTemplateCache.get(code);
}
@Override
public Long createNotifyTemplate(NotifyTemplateCreateReqVO createReqVO) {
// 校验站内信编码是否重复
checkNotifyTemplateCodeDuplicate(null, createReqVO.getCode());
// 插入
NotifyTemplateDO notifyTemplate = NotifyTemplateConvert.INSTANCE.convert(createReqVO);
notifyTemplate.setParams(parseTemplateContentParams(notifyTemplate.getContent()));
notifyTemplateMapper.insert(notifyTemplate);
// 发送刷新消息
notifyProducer.sendNotifyTemplateRefreshMessage();
return notifyTemplate.getId();
}
@Override
public void updateNotifyTemplate(NotifyTemplateUpdateReqVO updateReqVO) {
// 校验存在
validateNotifyTemplateExists(updateReqVO.getId());
// 校验站内信编码是否重复
checkNotifyTemplateCodeDuplicate(updateReqVO.getId(), updateReqVO.getCode());
// 更新
NotifyTemplateDO updateObj = NotifyTemplateConvert.INSTANCE.convert(updateReqVO);
updateObj.setParams(parseTemplateContentParams(updateObj.getContent()));
notifyTemplateMapper.updateById(updateObj);
// 发送刷新消息
notifyProducer.sendNotifyTemplateRefreshMessage();
}
@VisibleForTesting
public List<String> parseTemplateContentParams(String content) {
return ReUtil.findAllGroup1(PATTERN_PARAMS, content);
}
@Override
public void deleteNotifyTemplate(Long id) {
// 校验存在
validateNotifyTemplateExists(id);
// 删除
notifyTemplateMapper.deleteById(id);
// 发送刷新消息
notifyProducer.sendNotifyTemplateRefreshMessage();
}
private void validateNotifyTemplateExists(Long id) {
if (notifyTemplateMapper.selectById(id) == null) {
throw exception(NOTIFY_TEMPLATE_NOT_EXISTS);
}
}
@Override
public NotifyTemplateDO getNotifyTemplate(Long id) {
return notifyTemplateMapper.selectById(id);
}
@Override
public PageResult<NotifyTemplateDO> getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO) {
return notifyTemplateMapper.selectPage(pageReqVO);
}
@VisibleForTesting
public void checkNotifyTemplateCodeDuplicate(Long id, String code) {
NotifyTemplateDO template = notifyTemplateMapper.selectByCode(code);
if (template == null) {
return;
}
// 如果 id 为空,说明不用比较是否为相同 id 的字典类型
if (id == null) {
throw exception(NOTIFY_TEMPLATE_CODE_DUPLICATE, code);
}
if (!template.getId().equals(id)) {
throw exception(NOTIFY_TEMPLATE_CODE_DUPLICATE, code);
}
}
/**
*
*
* @param content
* @param params
* @return
*/
@Override
public String formatNotifyTemplateContent(String content, Map<String, Object> params) {
return StrUtil.format(content, params);
}
}

@ -145,7 +145,7 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest {
}
@Test
public void testBuildTemplateParams_paramMiss() {
public void testCheckTemplateParams_paramMiss() {
// 准备参数
MailTemplateDO template = randomPojo(MailTemplateDO.class,
o -> o.setParams(Lists.newArrayList("code")));
@ -153,7 +153,7 @@ class MailSendServiceImplTest extends BaseMockitoUnitTest {
// mock 方法
// 调用,并断言异常
assertServiceException(() -> mailSendService.buildTemplateParams(template, templateParams),
assertServiceException(() -> mailSendService.checkTemplateParams(template, templateParams),
MAIL_SEND_TEMPLATE_PARAM_MISS, "code");
}

@ -0,0 +1,266 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.enums.SqlConstants;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessageMyPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.message.NotifyMessagePageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper;
import com.baomidou.mybatisplus.annotation.DbType;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link NotifyMessageServiceImpl}
*
* @author
*/
@Import(NotifyMessageServiceImpl.class)
public class NotifyMessageServiceImplTest extends BaseDbUnitTest {
@Resource
private NotifyMessageServiceImpl notifyMessageService;
@Resource
private NotifyMessageMapper notifyMessageMapper;
@Test
public void testCreateNotifyMessage_success() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class);
String templateContent = randomString();
Map<String, Object> templateParams = randomTemplateParams();
// mock 方法
// 调用
Long messageId = notifyMessageService.createNotifyMessage(userId, userType,
template, templateContent, templateParams);
// 断言
NotifyMessageDO message = notifyMessageMapper.selectById(messageId);
assertNotNull(message);
assertEquals(userId, message.getUserId());
assertEquals(userType, message.getUserType());
assertEquals(template.getId(), message.getTemplateId());
assertEquals(template.getCode(), message.getTemplateCode());
assertEquals(template.getType(), message.getTemplateType());
assertEquals(template.getNickname(), message.getTemplateNickname());
assertEquals(templateContent, message.getTemplateContent());
assertEquals(templateParams, message.getTemplateParams());
assertEquals(false, message.getReadStatus());
assertNull(message.getReadTime());
}
@Test
public void testGetNotifyMessagePage() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setTemplateCode("test_01");
o.setTemplateType(10);
o.setCreateTime(buildTime(2022, 1, 2));
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 templateCode 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setTemplateCode("test_11")));
// 测试 templateType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setTemplateType(20)));
// 测试 createTime 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setCreateTime(buildTime(2022, 2, 1))));
// 准备参数
NotifyMessagePageReqVO reqVO = new NotifyMessagePageReqVO();
reqVO.setUserId(1L);
reqVO.setUserType(UserTypeEnum.ADMIN.getValue());
reqVO.setTemplateCode("est_01");
reqVO.setTemplateType(10);
reqVO.setCreateTime(buildBetweenTime(2022, 1, 1, 2022, 1, 10));
// 调用
PageResult<NotifyMessageDO> pageResult = notifyMessageService.getNotifyMessagePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotifyMessage, pageResult.getList().get(0));
}
@Test
public void testGetMyNotifyMessagePage() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(true);
o.setCreateTime(buildTime(2022, 1, 2));
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(false)));
// 测试 createTime 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setCreateTime(buildTime(2022, 2, 1))));
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
NotifyMessageMyPageReqVO reqVO = new NotifyMessageMyPageReqVO();
reqVO.setReadStatus(true);
reqVO.setCreateTime(buildBetweenTime(2022, 1, 1, 2022, 1, 10));
// 调用
PageResult<NotifyMessageDO> pageResult = notifyMessageService.getMyMyNotifyMessagePage(reqVO, userId, userType);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotifyMessage, pageResult.getList().get(0));
}
@Test
public void testGetUnreadNotifyMessageList() {
SqlConstants.init(DbType.MYSQL);
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(false);
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true)));
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
Integer size = 10;
// 调用
List<NotifyMessageDO> list = notifyMessageService.getUnreadNotifyMessageList(userId, userType, size);
// 断言
assertEquals(1, list.size());
assertPojoEquals(dbNotifyMessage, list.get(0));
}
@Test
public void testGetUnreadNotifyMessageCount() {
SqlConstants.init(DbType.MYSQL);
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(false);
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true)));
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
// 调用,并断言
assertEquals(1, notifyMessageService.getUnreadNotifyMessageCount(userId, userType));
}
@Test
public void testUpdateNotifyMessageRead() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(false);
o.setReadTime(null);
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true)));
// 准备参数
Collection<Long> ids = Arrays.asList(dbNotifyMessage.getId(), dbNotifyMessage.getId() + 1,
dbNotifyMessage.getId() + 2, dbNotifyMessage.getId() + 3);
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
// 调用
int updateCount = notifyMessageService.updateNotifyMessageRead(ids, userId, userType);
// 断言
assertEquals(1, updateCount);
NotifyMessageDO notifyMessage = notifyMessageMapper.selectById(dbNotifyMessage.getId());
assertTrue(notifyMessage.getReadStatus());
assertNotNull(notifyMessage.getReadTime());
}
@Test
public void testUpdateAllNotifyMessageRead() {
// mock 数据
NotifyMessageDO dbNotifyMessage = randomPojo(NotifyMessageDO.class, o -> { // 等会查询到
o.setUserId(1L);
o.setUserType(UserTypeEnum.ADMIN.getValue());
o.setReadStatus(false);
o.setReadTime(null);
o.setTemplateParams(randomTemplateParams());
});
notifyMessageMapper.insert(dbNotifyMessage);
// 测试 userId 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserId(2L)));
// 测试 userType 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setUserType(UserTypeEnum.MEMBER.getValue())));
// 测试 readStatus 不匹配
notifyMessageMapper.insert(cloneIgnoreId(dbNotifyMessage, o -> o.setReadStatus(true)));
// 准备参数
Long userId = 1L;
Integer userType = UserTypeEnum.ADMIN.getValue();
// 调用
int updateCount = notifyMessageService.updateAllNotifyMessageRead(userId, userType);
// 断言
assertEquals(1, updateCount);
NotifyMessageDO notifyMessage = notifyMessageMapper.selectById(dbNotifyMessage.getId());
assertTrue(notifyMessage.getReadStatus());
assertNotNull(notifyMessage.getReadTime());
}
private static Map<String, Object> randomTemplateParams() {
return MapUtil.<String, Object>builder().put(randomString(), randomString())
.put(randomString(), randomString()).build();
}
}

@ -0,0 +1,121 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailAccountDO;
import cn.iocoder.yudao.module.system.dal.dataobject.mail.MailTemplateDO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import org.assertj.core.util.Lists;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.HashMap;
import java.util.Map;
import static cn.hutool.core.util.RandomUtil.randomEle;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
class NotifySendServiceImplTest extends BaseMockitoUnitTest {
@InjectMocks
private NotifySendServiceImpl notifySendService;
@Mock
private NotifyTemplateService notifyTemplateService;
@Mock
private NotifyMessageService notifyMessageService;
/**
*
*/
@Test
public void testSendSingleNotify_successWhenMailTemplateEnable() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock NotifyTemplateService 的方法
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
String content = randomString();
when(notifyTemplateService.formatNotifyTemplateContent(eq(template.getContent()), eq(templateParams)))
.thenReturn(content);
// mock NotifyMessageService 的方法
Long messageId = randomLongId();
when(notifyMessageService.createNotifyMessage(eq(userId), eq(userType),
eq(template), eq(content), eq(templateParams))).thenReturn(messageId);
// 调用
Long resultMessageId = notifySendService.sendSingleNotify(userId, userType, templateCode, templateParams);
// 断言
assertEquals(messageId, resultMessageId);
}
/**
*
*/
@Test
public void testSendSingleMail_successWhenSmsTemplateDisable() {
// 准备参数
Long userId = randomLongId();
Integer userType = randomEle(UserTypeEnum.values()).getValue();
String templateCode = randomString();
Map<String, Object> templateParams = MapUtil.<String, Object>builder().put("code", "1234")
.put("op", "login").build();
// mock NotifyTemplateService 的方法
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class, o -> {
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
o.setContent("验证码为{code}, 操作为{op}");
o.setParams(Lists.newArrayList("code", "op"));
});
when(notifyTemplateService.getNotifyTemplateByCodeFromCache(eq(templateCode))).thenReturn(template);
// 调用
Long resultMessageId = notifySendService.sendSingleNotify(userId, userType, templateCode, templateParams);
// 断言
assertNull(resultMessageId);
verify(notifyTemplateService, never()).formatNotifyTemplateContent(anyString(), anyMap());
verify(notifyMessageService, never()).createNotifyMessage(anyLong(), anyInt(), any(), anyString(), anyMap());
}
@Test
public void testCheckMailTemplateValid_notExists() {
// 准备参数
String templateCode = randomString();
// mock 方法
// 调用,并断言异常
assertServiceException(() -> notifySendService.checkNotifyTemplateValid(templateCode),
NOTICE_NOT_FOUND);
}
@Test
public void testCheckTemplateParams_paramMiss() {
// 准备参数
NotifyTemplateDO template = randomPojo(NotifyTemplateDO.class,
o -> o.setParams(Lists.newArrayList("code")));
Map<String, Object> templateParams = new HashMap<>();
// mock 方法
// 调用,并断言异常
assertServiceException(() -> notifySendService.checkTemplateParams(template, templateParams),
NOTIFY_SEND_TEMPLATE_PARAM_MISS, "code");
}
}

@ -0,0 +1,146 @@
package cn.iocoder.yudao.module.system.service.notify;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplatePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.notify.vo.template.NotifyTemplateUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyTemplateMapper;
import cn.iocoder.yudao.module.system.mq.producer.notify.NotifyProducer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_TEMPLATE_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.verify;
/**
* {@link NotifyTemplateServiceImpl}
*
* @author
*/
@Import(NotifyTemplateServiceImpl.class)
public class NotifyTemplateServiceImplTest extends BaseDbUnitTest {
@Resource
private NotifyTemplateServiceImpl notifyTemplateService;
@Resource
private NotifyTemplateMapper notifyTemplateMapper;
@MockBean
private NotifyProducer notifyProducer;
@Test
public void testCreateNotifyTemplate_success() {
// 准备参数
NotifyTemplateCreateReqVO reqVO = randomPojo(NotifyTemplateCreateReqVO.class,
o -> o.setStatus(randomCommonStatus()));
// 调用
Long notifyTemplateId = notifyTemplateService.createNotifyTemplate(reqVO);
// 断言
assertNotNull(notifyTemplateId);
// 校验记录的属性是否正确
NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(notifyTemplateId);
assertPojoEquals(reqVO, notifyTemplate);
verify(notifyProducer).sendNotifyTemplateRefreshMessage();
}
@Test
public void testUpdateNotifyTemplate_success() {
// mock 数据
NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class);
notifyTemplateMapper.insert(dbNotifyTemplate);// @Sql: 先插入出一条存在的数据
// 准备参数
NotifyTemplateUpdateReqVO reqVO = randomPojo(NotifyTemplateUpdateReqVO.class, o -> {
o.setId(dbNotifyTemplate.getId()); // 设置更新的 ID
o.setStatus(randomCommonStatus());
});
// 调用
notifyTemplateService.updateNotifyTemplate(reqVO);
// 校验是否更新正确
NotifyTemplateDO notifyTemplate = notifyTemplateMapper.selectById(reqVO.getId()); // 获取最新的
assertPojoEquals(reqVO, notifyTemplate);
verify(notifyProducer).sendNotifyTemplateRefreshMessage();
}
@Test
public void testUpdateNotifyTemplate_notExists() {
// 准备参数
NotifyTemplateUpdateReqVO reqVO = randomPojo(NotifyTemplateUpdateReqVO.class);
// 调用, 并断言异常
assertServiceException(() -> notifyTemplateService.updateNotifyTemplate(reqVO), NOTIFY_TEMPLATE_NOT_EXISTS);
}
@Test
public void testDeleteNotifyTemplate_success() {
// mock 数据
NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class);
notifyTemplateMapper.insert(dbNotifyTemplate);// @Sql: 先插入出一条存在的数据
// 准备参数
Long id = dbNotifyTemplate.getId();
// 调用
notifyTemplateService.deleteNotifyTemplate(id);
// 校验数据不存在了
assertNull(notifyTemplateMapper.selectById(id));
verify(notifyProducer).sendNotifyTemplateRefreshMessage();
}
@Test
public void testDeleteNotifyTemplate_notExists() {
// 准备参数
Long id = randomLongId();
// 调用, 并断言异常
assertServiceException(() -> notifyTemplateService.deleteNotifyTemplate(id), NOTIFY_TEMPLATE_NOT_EXISTS);
}
@Test
public void testGetNotifyTemplatePage() {
// mock 数据
NotifyTemplateDO dbNotifyTemplate = randomPojo(NotifyTemplateDO.class, o -> { // 等会查询到
o.setName("芋头");
o.setCode("test_01");
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2022, 2, 3));
});
notifyTemplateMapper.insert(dbNotifyTemplate);
// 测试 name 不匹配
notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setName("投")));
// 测试 code 不匹配
notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCode("test_02")));
// 测试 status 不匹配
notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配
notifyTemplateMapper.insert(cloneIgnoreId(dbNotifyTemplate, o -> o.setCreateTime(buildTime(2022, 1, 5))));
// 准备参数
NotifyTemplatePageReqVO reqVO = new NotifyTemplatePageReqVO();
reqVO.setName("芋");
reqVO.setCode("est_01");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCreateTime(buildBetweenTime(2022, 2, 1, 2022, 2, 5));
// 调用
PageResult<NotifyTemplateDO> pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO);
// 断言
assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0));
}
}

@ -28,3 +28,5 @@ DELETE FROM "system_oauth2_code";
DELETE FROM "system_mail_account";
DELETE FROM "system_mail_template";
DELETE FROM "system_mail_log";
DELETE FROM "system_notify_template";
DELETE FROM "system_notify_message";

@ -626,3 +626,43 @@ CREATE TABLE IF NOT EXISTS "system_mail_log" (
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '';
-- SQL yudao-module-system-biz test/resources/sql/create_tables.sql
CREATE TABLE IF NOT EXISTS "system_notify_template" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"name" varchar NOT NULL,
"code" varchar NOT NULL,
"nickname" varchar NOT NULL,
"content" varchar NOT NULL,
"type" varchar NOT NULL,
"params" varchar,
"status" varchar NOT NULL,
"remark" varchar,
"creator" varchar DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
PRIMARY KEY ("id")
) COMMENT '';
CREATE TABLE IF NOT EXISTS "system_notify_message" (
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
"user_id" bigint NOT NULL,
"user_type" varchar NOT NULL,
"template_id" bigint NOT NULL,
"template_code" varchar NOT NULL,
"template_nickname" varchar NOT NULL,
"template_content" varchar NOT NULL,
"template_type" int NOT NULL,
"template_params" varchar NOT NULL,
"read_status" bit NOT NULL,
"read_time" varchar,
"creator" varchar DEFAULT '',
"create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updater" varchar DEFAULT '',
"update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
"deleted" bit NOT NULL DEFAULT FALSE,
"tenant_id" bigint not null default '0',
PRIMARY KEY ("id")
) COMMENT '';

@ -21,4 +21,10 @@ public class DefaultController {
"[工作流模块 yudao-module-bpm - 已禁用][参考 https://doc.iocoder.cn/bpm/ 开启]");
}
@RequestMapping("/admin-api/mp/**")
public CommonResult<Boolean> mp404() {
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[微信公众号 yudao-module-mp - 已禁用][参考 https://doc.iocoder.cn/mp/build/ 开启]");
}
}

@ -133,6 +133,7 @@ yudao:
- cn.iocoder.yudao.module.member.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.system.enums.ErrorCodeConstants
- cn.iocoder.yudao.module.mp.enums.ErrorCodeConstants
tenant: # 多租户相关配置项
enable: true
ignore-urls:
@ -159,6 +160,7 @@ yudao:
- system_mail_account
- system_mail_template
- system_mail_log
- system_notify_template
- infra_codegen_column
- infra_codegen_table
- infra_test_demo

@ -1,6 +1,6 @@
{
"name": "yudao-ui-admin-vue3",
"version": "1.6.6-snapshot.1925",
"version": "1.7.0-snapshot.1925",
"description": "基于vue3、vite4、element-plus、typesScript",
"author": "xingyu",
"private": false,

@ -21,7 +21,7 @@ export interface MailTemplatePageReqVO extends PageParam {
createTime?: Date[]
}
export interface MailSmsReqVO {
export interface MailSendReqVO {
mail: string
templateCode: string
templateParams: Map<String, Object>
@ -53,6 +53,6 @@ export const deleteMailTemplateApi = async (id: number) => {
}
// 发送邮件
export const sendMailApi = (data: MailSmsReqVO) => {
export const sendMailApi = (data: MailSendReqVO) => {
return request.post({ url: '/system/mail-template/send-mail', data })
}

@ -0,0 +1,66 @@
import request from '@/config/axios'
import qs from 'qs'
export interface NotifyMessageVO {
id: number
userId: number
userType: number
templateId: number
templateCode: string
templateNickname: string
templateContent: string
templateType: number
templateParams: string
readStatus: boolean
readTime: Date
}
export interface NotifyMessagePageReqVO extends PageParam {
userId?: number
userType?: number
templateCode?: string
templateType?: number
createTime?: Date[]
}
export interface NotifyMessageMyPageReqVO extends PageParam {
readStatus?: boolean
createTime?: Date[]
}
// 查询站内信消息列表
export const getNotifyMessagePageApi = async (params: NotifyMessagePageReqVO) => {
return await request.get({ url: '/system/notify-message/page', params })
}
// 查询站内信消息详情
export const getNotifyMessageApi = async (id: number) => {
return await request.get({ url: '/system/notify-message/get?id=' + id })
}
// 获得我的站内信分页
export const getMyNotifyMessagePage = async (params: NotifyMessageMyPageReqVO) => {
return await request.get({ url: '/system/notify-message/my-page', params })
}
// 批量标记已读
export const updateNotifyMessageRead = async (ids) => {
return await request.put({
url: '/system/notify-message/update-read?' + qs.stringify({ ids: ids }, { indices: false })
})
}
// 标记所有站内信为已读
export const updateAllNotifyMessageRead = async () => {
return await request.put({ url: '/system/notify-message/update-all-read' })
}
// 获取当前用户的最新站内信列表
export const getUnreadNotifyMessageListApi = async () => {
return await request.get({ url: '/system/notify-message/get-unread-list' })
}
// 获得当前用户的未读站内信数量
export const getUnreadNotifyMessageCountApi = async () => {
return await request.get({ url: '/system/notify-message/get-unread-count' })
}

@ -0,0 +1,55 @@
import request from '@/config/axios'
export interface NotifyTemplateVO {
id: number
name: string
code: string
content: string
type: number
params: string
status: number
remark: string
}
export interface NotifyTemplatePageReqVO extends PageParam {
name?: string
code?: string
status?: number
createTime?: Date[]
}
export interface NotifySendReqVO {
userId: number
templateCode: string
templateParams: Map<String, Object>
}
// 查询站内信模板列表
export const getNotifyTemplatePageApi = async (params: NotifyTemplatePageReqVO) => {
return await request.get({ url: '/system/notify-template/page', params })
}
// 查询站内信模板详情
export const getNotifyTemplateApi = async (id: number) => {
return await request.get({ url: '/system/notify-template/get?id=' + id })
}
// 新增站内信模板
export const createNotifyTemplateApi = async (data: NotifyTemplateVO) => {
return await request.post({ url: '/system/notify-template/create', data })
}
// 修改站内信模板
export const updateNotifyTemplateApi = async (data: NotifyTemplateVO) => {
return await request.put({ url: '/system/notify-template/update', data })
}
// 删除站内信模板
export const deleteNotifyTemplateApi = async (id: number) => {
return await request.delete({ url: '/system/notify-template/delete?id=' + id })
}
// 发送站内信
export const sendNotifyApi = (data: NotifySendReqVO) => {
return request.post({ url: '/system/notify-template/send-notify', data })
}

@ -7,7 +7,7 @@ export interface tableMethod {
deleteBatch: () => void // 批量删除
exportList: (fileName?: string) => void // 导出列表
getCurrentColumn: () => void // 获取当前列
getRadioRecord: () => void // 获取当前选中列redio
getRadioRecord: () => void // 获取当前选中列radio
getCheckboxRecords: () => void //获取当前选中列, checkbox
}

@ -1,77 +1,74 @@
<script setup lang="ts">
import dayjs from 'dayjs'
import * as NotifyMessageApi from '@/api/system/notify/message'
const { push } = useRouter()
const activeName = ref('notice')
const unreadCount = ref(0) //
const list = ref([]) //
//
const getList = async () => {
list.value = await NotifyMessageApi.getUnreadNotifyMessageListApi()
// unreadCount 0
unreadCount.value = 0
}
//
const getUnreadCount = async () => {
NotifyMessageApi.getUnreadNotifyMessageCountApi().then((data) => {
unreadCount.value = data
})
}
//
const goMyList = () => {
push({
name: 'MyNotifyMessage'
})
}
const noticeList = ref([
{ id: 1, title: '版本升级1', date: '2022-12-12 10:00:00' },
{ id: 2, title: '版本升级2', date: '2022-12-12 10:00:00' },
{ id: 3, title: '版本升级3', date: '2022-12-12 10:00:00' },
{ id: 4, title: '版本升级4', date: '2022-12-12 10:00:00' },
{ id: 5, title: '版本升级5', date: '2022-12-12 10:00:00' }
])
const messageList = ref([
{ id: 1, title: '加班1', date: '2022-12-12 10:00:00' },
{ id: 2, title: '加班2', date: '2022-12-12 10:00:00' },
{ id: 3, title: '加班3', date: '2022-12-12 10:00:00' },
{ id: 4, title: '加班4', date: '2022-12-12 10:00:00' },
{ id: 5, title: '加班5', date: '2022-12-12 10:00:00' }
])
const needList = ref([
{ id: 1, title: '审批1', date: '2022-12-12 10:00:00' },
{ id: 2, title: '审批2', date: '2022-12-12 10:00:00' },
{ id: 3, title: '审批3', date: '2022-12-12 10:00:00' },
{ id: 4, title: '审批4', date: '2022-12-12 10:00:00' },
{ id: 5, title: '审批5', date: '2022-12-12 10:00:00' }
])
// ========== =========
onMounted(() => {
//
getUnreadCount()
//
setInterval(() => {
getUnreadCount()
}, 1000 * 60 * 2)
})
</script>
<template>
<div class="message">
<ElPopover placement="bottom" :width="310" trigger="click">
<ElPopover placement="bottom" :width="400" trigger="click">
<template #reference>
<ElBadge :value="noticeList.length" class="item">
<Icon icon="ep:bell" :size="18" class="cursor-pointer" />
<ElBadge :is-dot="unreadCount > 0" class="item">
<Icon icon="ep:bell" :size="18" class="cursor-pointer" @click="getList" />
</ElBadge>
</template>
<ElTabs v-model="activeName">
<ElTabPane label="通知(5)" name="notice">
<div class="message-list">
<template v-for="item in noticeList" :key="item.id">
<div class="message-item">
<img src="@/assets/imgs/avatar.gif" alt="" class="message-icon" />
<div class="message-content">
<span class="message-title">{{ item.title }}</span>
<span class="message-date">{{ item.date }}</span>
</div>
</div>
</template>
</div>
</ElTabPane>
<ElTabPane label="消息(0)" name="message">
<ElTabPane label="我的站内信" name="notice">
<div class="message-list">
<template v-for="item in messageList" :key="item.id">
<template v-for="item in list" :key="item.id">
<div class="message-item">
<img src="@/assets/imgs/avatar.gif" alt="" class="message-icon" />
<div class="message-content">
<span class="message-title">{{ item.title }}</span>
<span class="message-date">{{ item.date }}</span>
</div>
</div>
</template>
</div>
</ElTabPane>
<ElTabPane label="代办(0)" name="need">
<div class="message-list">
<template v-for="item in needList" :key="item.id">
<div class="message-item">
<img src="@/assets/imgs/avatar.gif" alt="" class="message-icon" />
<div class="message-content">
<span class="message-title">{{ item.title }}</span>
<span class="message-date">{{ item.date }}</span>
<span class="message-title">
{{ item.templateNickname }}{{ item.templateContent }}
</span>
<span class="message-date">
{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</span>
</div>
</div>
</template>
</div>
</ElTabPane>
</ElTabs>
<!-- 更多 -->
<div style="text-align: right; margin-top: 10px">
<XButton type="primary" preIcon="ep:view" title="查看全部" @click="goMyList" />
</div>
</ElPopover>
</div>
</template>

@ -41,7 +41,7 @@ const loginOut = () => {
.catch(() => {})
}
const toProfile = async () => {
push('/userinfo/profile')
push('/user/profile')
}
const toDocument = () => {
window.open('https://doc.iocoder.cn/')

@ -71,7 +71,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
]
},
{
path: '/userinfo',
path: '/user',
component: Layout,
name: 'UserInfo',
meta: {
@ -89,6 +89,18 @@ const remainingRouter: AppRouteRecordRaw[] = [
icon: 'ep:user',
title: t('common.profile')
}
},
{
path: 'notify-message',
component: () => import('@/views/system/notify/my/index.vue'),
name: 'MyNotifyMessage',
meta: {
canTo: true,
hidden: true,
noTagsView: false,
icon: 'ep:message',
title: '我的站内信'
}
}
]
},

@ -91,6 +91,7 @@ export enum DICT_TYPE {
SYSTEM_ERROR_CODE_TYPE = 'system_error_code_type',
SYSTEM_OAUTH2_GRANT_TYPE = 'system_oauth2_grant_type',
SYSTEM_MAIL_SEND_STATUS = 'system_mail_send_status',
SYSTEM_NOTIFY_TEMPLATE_TYPE = 'system_notify_template_type',
// ========== INFRA 模块 ==========
INFRA_BOOLEAN_STRING = 'infra_boolean_string',

@ -252,7 +252,7 @@ const handleSendMail = (row: any) => {
}
const sendTest = async () => {
const data: MailTemplateApi.MailSmsReqVO = {
const data: MailTemplateApi.MailSendReqVO = {
mail: sendForm.value.mail,
templateCode: sendForm.value.templateCode,
templateParams: sendForm.value.templateParams as unknown as Map<string, Object>

@ -0,0 +1,66 @@
<template>
<ContentWrap>
<!-- 列表 -->
<XTable @register="registerTable">
<template #actionbtns_default="{ row }">
<!-- 操作详情 -->
<XTextButton
preIcon="ep:view"
:title="t('action.detail')"
v-hasPermi="['system:notify-message:query']"
@click="handleDetail(row.id)"
/>
</template>
</XTable>
</ContentWrap>
<!-- 弹窗 -->
<XModal id="messageModel" :loading="modelLoading" v-model="modelVisible" :title="modelTitle">
<!-- 表单详情 -->
<Descriptions
v-if="actionType === 'detail'"
:schema="allSchemas.detailSchema"
:data="detailData"
/>
<template #footer>
<!-- 按钮关闭 -->
<XButton :loading="actionLoading" :title="t('dialog.close')" @click="modelVisible = false" />
</template>
</XModal>
</template>
<script setup lang="ts" name="NotifyMessage">
// import
import { allSchemas } from './message.data'
import * as NotifyMessageApi from '@/api/system/notify/message'
const { t } = useI18n() //
//
const [registerTable] = useXTable({
allSchemas: allSchemas,
getListApi: NotifyMessageApi.getNotifyMessagePageApi
})
//
const modelVisible = ref(false) //
const modelTitle = ref('edit') //
const modelLoading = ref(false) // loading
const actionType = ref('') //
const actionLoading = ref(false) // Loading
const detailData = ref() // Ref
//
const setDialogTile = (type: string) => {
modelLoading.value = true
modelTitle.value = t('action.' + type)
actionType.value = type
modelVisible.value = true
}
//
const handleDetail = async (rowId: number) => {
setDialogTile('detail')
const res = await NotifyMessageApi.getNotifyMessageApi(rowId)
detailData.value = res
modelLoading.value = false
}
</script>

@ -0,0 +1,101 @@
import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
// CrudSchema
const crudSchemas = reactive<VxeCrudSchema>({
primaryKey: 'id', // 默认的主键ID
primaryTitle: '编号', // 默认显示的值
primaryType: 'seq', // 默认为seq序号模式
action: true,
actionWidth: '200', // 3个按钮默认200如有删减对应增减即可
columns: [
{
title: '用户编号',
field: 'userId',
isSearch: true
},
{
title: '用户类型',
field: 'userType',
dictType: DICT_TYPE.USER_TYPE,
dictClass: 'string',
isSearch: true,
table: {
width: 80
}
},
{
title: '模版编号',
field: 'templateId'
},
{
title: '模板编码',
field: 'templateCode',
isSearch: true,
table: {
width: 80
}
},
{
title: '发送人名称',
field: 'templateNickname',
table: {
width: 120
}
},
{
title: '模版内容',
field: 'templateContent',
table: {
width: 200
}
},
{
title: '模版类型',
field: 'templateType',
dictType: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE,
dictClass: 'number',
isSearch: true,
table: {
width: 80
}
},
{
title: '模版参数',
field: 'templateParams',
isTable: false
},
{
title: '是否已读',
field: 'readStatus',
dictType: DICT_TYPE.INFRA_BOOLEAN_STRING,
dictClass: 'boolean',
table: {
width: 80
}
},
{
title: '阅读时间',
field: 'readTime',
formatter: 'formatDate',
table: {
width: 180
}
},
{
title: '创建时间',
field: 'createTime',
isForm: false,
formatter: 'formatDate',
search: {
show: true,
itemRender: {
name: 'XDataTimePicker'
}
},
table: {
width: 180
}
}
]
})
export const { allSchemas } = useVxeCrudSchemas(crudSchemas)

@ -0,0 +1,58 @@
<template>
<ContentWrap>
<!-- 列表 -->
<XTable @register="registerTable">
<template #toolbar_buttons>
<!-- 操作标记已读 -->
<XButton type="primary" preIcon="ep:zoom-in" title="标记已读" @click="handleUpdateList" />
<!-- 操作全部已读 -->
<XButton type="primary" preIcon="ep:zoom-in" title="全部已读" @click="handleUpdateAll" />
</template>
<template #actionbtns_default="{ row }">
<!-- 操作已读 -->
<XTextButton
preIcon="ep:view"
title="已读"
v-hasPermi="['system:notify-message:query']"
v-if="!row.readStatus"
@click="handleUpdate([row.id])"
/>
</template>
</XTable>
</ContentWrap>
</template>
<script setup lang="ts" name="MyNotifyMessage">
// import
import { allSchemas } from './my.data'
import * as NotifyMessageApi from '@/api/system/notify/message'
const message = useMessage() //
//
const [registerTable, { reload, getCheckboxRecords }] = useXTable({
allSchemas: allSchemas,
getListApi: NotifyMessageApi.getMyNotifyMessagePage
})
const handleUpdateList = async () => {
const list = getCheckboxRecords()
if (list.length === 0) {
return
}
await handleUpdate(list.map((v) => v.id))
}
// id
const handleUpdate = async (ids) => {
await NotifyMessageApi.updateNotifyMessageRead(ids)
message.success('标记已读成功!')
reload()
}
//
const handleUpdateAll = async () => {
await NotifyMessageApi.updateAllNotifyMessageRead()
message.success('全部已读成功!')
reload()
}
</script>

@ -0,0 +1,58 @@
import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
// CrudSchema
const crudSchemas = reactive<VxeCrudSchema>({
primaryKey: 'id',
primaryTitle: ' ',
primaryType: 'checkbox',
action: true,
actionWidth: '200', // 3个按钮默认200如有删减对应增减即可
columns: [
{
title: '发送人名称',
field: 'templateNickname',
table: {
width: 120
}
},
{
title: '发送时间',
field: 'createTime',
isForm: false,
formatter: 'formatDate',
search: {
show: true,
itemRender: {
name: 'XDataTimePicker'
}
},
table: {
width: 180
}
},
{
title: '类型',
field: 'templateType',
dictType: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE,
dictClass: 'number',
table: {
width: 80
}
},
{
title: '内容',
field: 'templateContent'
},
{
title: '是否已读',
field: 'readStatus',
dictType: DICT_TYPE.INFRA_BOOLEAN_STRING,
dictClass: 'boolean',
table: {
width: 80
},
isSearch: true
}
]
})
export const { allSchemas } = useVxeCrudSchemas(crudSchemas)

@ -0,0 +1,251 @@
<template>
<ContentWrap>
<!-- 列表 -->
<XTable @register="registerTable">
<template #toolbar_buttons>
<!-- 操作新增 -->
<XButton
type="primary"
preIcon="ep:zoom-in"
:title="t('action.add')"
v-hasPermi="['system:notify-template:create']"
@click="handleCreate()"
/>
</template>
<template #actionbtns_default="{ row }">
<!-- 操作测试站内信 -->
<XTextButton
preIcon="ep:cpu"
:title="t('action.test')"
v-hasPermi="['system:notify-template:send-notify']"
@click="handleSendNotify(row)"
/>
<!-- 操作修改 -->
<XTextButton
preIcon="ep:edit"
:title="t('action.edit')"
v-hasPermi="['system:notify-template:update']"
@click="handleUpdate(row.id)"
/>
<!-- 操作详情 -->
<XTextButton
preIcon="ep:view"
:title="t('action.detail')"
v-hasPermi="['system:notify-template:query']"
@click="handleDetail(row.id)"
/>
<!-- 操作删除 -->
<XTextButton
preIcon="ep:delete"
:title="t('action.del')"
v-hasPermi="['system:notify-template:delete']"
@click="deleteData(row.id)"
/>
</template>
</XTable>
</ContentWrap>
<!-- 添加/修改的弹窗 -->
<XModal id="templateModel" :loading="modelLoading" v-model="modelVisible" :title="modelTitle">
<!-- 表单添加/修改 -->
<Form
ref="formRef"
v-if="['create', 'update'].includes(actionType)"
:schema="allSchemas.formSchema"
:rules="rules"
/>
<!-- 表单详情 -->
<Descriptions
v-if="actionType === 'detail'"
:schema="allSchemas.detailSchema"
:data="detailData"
/>
<template #footer>
<!-- 按钮保存 -->
<XButton
v-if="['create', 'update'].includes(actionType)"
type="primary"
:title="t('action.save')"
:loading="actionLoading"
@click="submitForm()"
/>
<!-- 按钮关闭 -->
<XButton :loading="actionLoading" :title="t('dialog.close')" @click="modelVisible = false" />
</template>
</XModal>
<!-- 测试站内信的弹窗 -->
<XModal id="sendTest" v-model="sendVisible" title="测试">
<el-form :model="sendForm" :rules="sendRules" label-width="200px" label-position="top">
<el-form-item label="模板内容" prop="content">
<el-input type="textarea" v-model="sendForm.content" readonly />
</el-form-item>
<el-form-item label="接收人" prop="userId">
<el-select v-model="sendForm.userId" placeholder="请选择接收人">
<el-option
v-for="item in userOption"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item
v-for="param in sendForm.params"
:key="param"
:label="'参数 {' + param + '}'"
:prop="'templateParams.' + param"
>
<el-input
v-model="sendForm.templateParams[param]"
:placeholder="'请输入 ' + param + ' 参数'"
/>
</el-form-item>
</el-form>
<!-- 操作按钮 -->
<template #footer>
<XButton
type="primary"
:title="t('action.test')"
:loading="actionLoading"
@click="sendTest()"
/>
<XButton :title="t('dialog.close')" @click="sendVisible = false" />
</template>
</XModal>
</template>
<script setup lang="ts" name="NotifyTemplate">
import { FormExpose } from '@/components/Form'
// import
import { rules, allSchemas } from './template.data'
import * as NotifyTemplateApi from '@/api/system/notify/template'
import { getListSimpleUsersApi, UserVO } from '@/api/system/user'
const { t } = useI18n() //
const message = useMessage() //
//
const [registerTable, { reload, deleteData }] = useXTable({
allSchemas: allSchemas,
getListApi: NotifyTemplateApi.getNotifyTemplatePageApi,
deleteApi: NotifyTemplateApi.deleteNotifyTemplateApi
})
//
const modelVisible = ref(false) //
const modelTitle = ref('edit') //
const modelLoading = ref(false) // loading
const actionType = ref('') //
const actionLoading = ref(false) // Loading
const formRef = ref<FormExpose>() // Ref
const detailData = ref() // Ref
//
const setDialogTile = (type: string) => {
modelLoading.value = true
modelTitle.value = t('action.' + type)
actionType.value = type
modelVisible.value = true
}
//
const handleCreate = () => {
setDialogTile('create')
modelLoading.value = false
}
//
const handleUpdate = async (rowId: number) => {
setDialogTile('update')
//
const res = await NotifyTemplateApi.getNotifyTemplateApi(rowId)
unref(formRef)?.setValues(res)
modelLoading.value = false
}
//
const handleDetail = async (rowId: number) => {
setDialogTile('detail')
const res = await NotifyTemplateApi.getNotifyTemplateApi(rowId)
detailData.value = res
modelLoading.value = false
}
//
const submitForm = async () => {
const elForm = unref(formRef)?.getElFormRef()
if (!elForm) return
elForm.validate(async (valid) => {
if (valid) {
actionLoading.value = true
//
try {
const data = unref(formRef)?.formModel as NotifyTemplateApi.NotifyTemplateVO
if (actionType.value === 'create') {
await NotifyTemplateApi.createNotifyTemplateApi(data)
message.success(t('common.createSuccess'))
} else {
await NotifyTemplateApi.updateNotifyTemplateApi(data)
message.success(t('common.updateSuccess'))
}
modelVisible.value = false
} finally {
actionLoading.value = false
//
await reload()
}
}
})
}
// ========== ==========
const sendForm = ref({
content: '',
params: {},
userId: undefined,
templateCode: '',
templateParams: {}
})
const sendRules = ref({
userId: [{ required: true, message: '用户编号不能为空', trigger: 'blur' }],
templateCode: [{ required: true, message: '模版编号不能为空', trigger: 'blur' }],
templateParams: {}
})
const sendVisible = ref(false)
const userOption = ref<UserVO[]>([])
const handleSendNotify = (row: any) => {
sendForm.value.content = row.content
sendForm.value.params = row.params
sendForm.value.templateCode = row.code
sendForm.value.templateParams = row.params.reduce(function (obj, item) {
obj[item] = undefined
return obj
}, {})
sendRules.value.templateParams = row.params.reduce(function (obj, item) {
obj[item] = { required: true, message: '参数 ' + item + ' 不能为空', trigger: 'change' }
return obj
}, {})
sendVisible.value = true
}
const sendTest = async () => {
const data: NotifyTemplateApi.NotifySendReqVO = {
userId: sendForm.value.userId,
templateCode: sendForm.value.templateCode,
templateParams: sendForm.value.templateParams as unknown as Map<string, Object>
}
const res = await NotifyTemplateApi.sendNotifyApi(data)
if (res) {
message.success('提交发送成功!发送结果,见发送日志编号:' + res)
}
sendVisible.value = false
}
// ========== ==========
onMounted(() => {
getListSimpleUsersApi().then((data) => {
userOption.value = data
})
})
</script>

@ -0,0 +1,85 @@
import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
// 表单校验
export const rules = reactive({
name: [required],
code: [required],
content: [required],
type: [required],
status: [required]
})
// CrudSchema
const crudSchemas = reactive<VxeCrudSchema>({
primaryKey: 'id',
primaryTitle: '编号',
primaryType: null,
action: true,
actionWidth: '260', // 3个按钮默认200如有删减对应增减即可
columns: [
{
title: '模版编码',
field: 'code',
isSearch: true
},
{
title: '模板名称',
field: 'name',
isSearch: true
},
{
title: '发件人名称',
field: 'nickname'
},
{
title: '类型',
field: 'type',
dictType: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE,
dictClass: 'number'
},
{
title: '模版内容',
field: 'content',
table: {
width: 300
},
form: {
component: 'Input',
componentProps: {
type: 'textarea',
rows: 4
},
colProps: {
span: 24
}
}
},
{
title: '状态',
field: 'status',
dictType: DICT_TYPE.COMMON_STATUS,
dictClass: 'number',
isSearch: true
},
{
title: '备注',
field: 'remark'
},
{
title: '创建时间',
field: 'createTime',
isForm: false,
formatter: 'formatDate',
search: {
show: true,
itemRender: {
name: 'XDataTimePicker'
}
},
table: {
width: 180
}
}
]
})
export const { allSchemas } = useVxeCrudSchemas(crudSchemas)

@ -1,6 +1,6 @@
{
"name": "yudao-ui-admin",
"version": "1.6.6-snapshot",
"version": "1.7.0-snapshot",
"description": "芋道管理系统",
"author": "芋道",
"license": "MIT",

@ -0,0 +1,52 @@
import request from '@/utils/request'
import qs from 'qs'
// 获得我的站内信分页
export function getNotifyMessagePage(query) {
return request({
url: '/system/notify-message/page',
method: 'get',
params: query
})
}
// 获得我的站内信分页
export function getMyNotifyMessagePage(query) {
return request({
url: '/system/notify-message/my-page',
method: 'get',
params: query
})
}
// 批量标记已读
export function updateNotifyMessageRead(ids) {
return request({
url: '/system/notify-message/update-read?' + qs.stringify({ids: ids}, { indices: false }),
method: 'put'
})
}
// 标记所有站内信为已读
export function updateAllNotifyMessageRead() {
return request({
url: '/system/notify-message/update-all-read',
method: 'put'
})
}
// 获取当前用户的最新站内信列表
export function getUnreadNotifyMessageList() {
return request({
url: '/system/notify-message/get-unread-list',
method: 'get'
})
}
// 获得当前用户的未读站内信数量
export function getUnreadNotifyMessageCount() {
return request({
url: '/system/notify-message/get-unread-count',
method: 'get'
})
}

@ -0,0 +1,64 @@
import request from '@/utils/request'
// 创建站内信模板
export function createNotifyTemplate(data) {
return request({
url: '/system/notify-template/create',
method: 'post',
data: data
})
}
// 更新站内信模板
export function updateNotifyTemplate(data) {
return request({
url: '/system/notify-template/update',
method: 'put',
data: data
})
}
// 删除站内信模板
export function deleteNotifyTemplate(id) {
return request({
url: '/system/notify-template/delete?id=' + id,
method: 'delete'
})
}
// 获得站内信模板
export function getNotifyTemplate(id) {
return request({
url: '/system/notify-template/get?id=' + id,
method: 'get'
})
}
// 获得站内信模板分页
export function getNotifyTemplatePage(query) {
return request({
url: '/system/notify-template/page',
method: 'get',
params: query
})
}
// 创建站内信模板
export function sendNotify(data) {
return request({
url: '/system/notify-template/send-notify',
method: 'post',
data: data
})
}
// 导出站内信模板 Excel
export function exportNotifyTemplateExcel(query) {
return request({
url: '/system/notify-template/export-excel',
method: 'get',
params: query,
responseType: 'blob'
})
}

@ -1,5 +1,9 @@
<template>
<el-alert v-if="enable()" :title="'【' + title + '】文档地址:' + url" type="success" show-icon />
<el-alert v-if="enable()" type="success" show-icon>
<template slot="title">
<div @click="goToUrl">{{ '' + title + '' + url }}</div>
</template>
</el-alert>
</template>
<script>
@ -14,6 +18,9 @@ export default {
methods: {
enable: function () {
return getDocEnable();
},
goToUrl: function() {
window.open(this.url);
}
}
};

@ -9,7 +9,7 @@ export default {
name: 'YudaoDoc',
data() {
return {
url: 'http://www.iocoder.cn/Yudao/build-debugger-environment/?yudao'
url: 'https://doc.iocoder.cn/'
}
},
methods: {

@ -0,0 +1,83 @@
<template>
<div>
<el-popover placement="bottom" width="600" trigger="click">
<!-- icon 展示 -->
<el-badge slot="reference" :is-dot="unreadCount > 0" type="danger">
<svg-icon icon-class="message" @click="getList"/>
</el-badge>
<!-- 弹出列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column width="120" property="templateNickname" label="发送人" />
<el-table-column width="180" property="createTime" label="发送时间">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="类型" align="center" prop="templateType" width="100">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE" :value="scope.row.templateType" />
</template>
</el-table-column>
<el-table-column property="templateContent" label="内容" />
</el-table>
<!-- 更多 -->
<div style="text-align: right; margin-top: 10px">
<el-button type="primary" size="mini" @click="goMyList"></el-button>
</div>
</el-popover>
</div>
</template>
<script>
import {getUnreadNotifyMessageCount, getUnreadNotifyMessageList} from "@/api/system/notify/message";
export default {
name: 'NotifyMessage',
data() {
return {
//
loading: false,
//
list: [],
// ,
unreadCount: 0,
}
},
created() {
//
this.getUnreadCount()
//
setInterval(() => {
this.getUnreadCount()
},1000 * 60 * 2)
},
methods: {
getList: function() {
this.loading = true;
getUnreadNotifyMessageList().then(response => {
this.list = response.data;
this.loading = false;
// unreadCount 0
this.unreadCount = 0
});
},
getUnreadCount: function() {
getUnreadNotifyMessageCount().then(response => {
this.unreadCount = response.data;
})
},
goMyList: function() {
this.$router.push({
name: 'MyNotifyMessage'
});
}
}
}
</script>
<style>
.el-badge__content.is-fixed {
top: 10px; /* 保证徽章的位置 */
}
</style>

@ -9,6 +9,9 @@
<template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" />
<!-- 站内信 -->
<notify-message class="right-menu-item hover-effect" />
<el-tooltip content="源码地址" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
@ -57,6 +60,7 @@ import SizeSelect from '@/components/SizeSelect'
import Search from '@/components/HeaderSearch'
import RuoYiGit from '@/components/RuoYi/Git'
import RuoYiDoc from '@/components/RuoYi/Doc'
import NotifyMessage from '@/layout/components/Message'
import {getPath} from "@/utils/ruoyi";
export default {
@ -68,7 +72,8 @@ export default {
SizeSelect,
Search,
RuoYiGit,
RuoYiDoc
RuoYiDoc,
NotifyMessage
},
computed: {
...mapGetters([

@ -75,7 +75,8 @@ export const constantRoutes = [
meta: {title: '首页', icon: 'dashboard', affix: true}
}
]
}, {
},
{
path: '/user',
component: Layout,
hidden: true,
@ -85,9 +86,14 @@ export const constantRoutes = [
component: (resolve) => require(['@/views/system/user/profile/index'], resolve),
name: 'Profile',
meta: {title: '个人中心', icon: 'user'}
}
]
}, {
path: 'notify-message',
component: (resolve) => require(['@/views/system/notify/my/index'], resolve),
name: 'MyNotifyMessage',
meta: { title: '我的站内信', icon: 'message' },
}]
},
{
path: '/dict',
component: Layout,
hidden: true,
@ -98,18 +104,8 @@ export const constantRoutes = [
meta: {title: '字典数据', icon: '', activeMenu: '/system/dict'}
}
]
}, {
path: '/property',
component: Layout,
hidden: true,
children: [{
path: 'value/:propertyId(\\d+)',
component: (resolve) => require(['@/views/mall/product/property/value'], resolve),
name: 'PropertyValue',
meta: {title: '商品属性值', icon: '', activeMenu: '/product/property'}
}
]
}, {
},
{
path: '/job',
component: Layout,
hidden: true,
@ -131,24 +127,8 @@ export const constantRoutes = [
meta: {title: '修改生成配置', activeMenu: '/infra/codegen'}
}
]
}, {
path: '/spu',
component: Layout,
hidden: true,
children: [{
path: 'edit/:spuId(\\d+)',
component: (resolve) => require(['@/views/mall/product/spu/save'], resolve),
name: 'SpuEdit',
meta: {title: '修改商品', activeMenu: '/product/spu'}
},
{
path: 'add',
component: (resolve) => require(['@/views/mall/product/spu/save'], resolve),
name: 'SpuAdd',
meta: {title: '添加商品', activeMenu: '/product/spu'}
}
]
}, {
path: '/bpm',
component: Layout,
hidden: true,
@ -165,7 +145,8 @@ export const constantRoutes = [
meta: {title: '查看 OA 请假', icon: 'view', activeMenu: '/bpm/oa/leave'}
}
]
}, {
},
{
path: '/bpm',
component: Layout,
hidden: true,
@ -197,6 +178,36 @@ export const constantRoutes = [
}
]
},
{
path: '/property',
component: Layout,
hidden: true,
children: [{
path: 'value/:propertyId(\\d+)',
component: (resolve) => require(['@/views/mall/product/property/value'], resolve),
name: 'PropertyValue',
meta: {title: '商品属性值', icon: '', activeMenu: '/product/property'}
}
]
},
{
path: '/spu',
component: Layout,
hidden: true,
children: [{
path: 'edit/:spuId(\\d+)',
component: (resolve) => require(['@/views/mall/product/spu/save'], resolve),
name: 'SpuEdit',
meta: {title: '修改商品', activeMenu: '/product/spu'}
},
{
path: 'add',
component: (resolve) => require(['@/views/mall/product/spu/save'], resolve),
name: 'SpuAdd',
meta: {title: '添加商品', activeMenu: '/product/spu'}
}
]
},
{
path: '/trade/order',
component: Layout,

@ -26,6 +26,7 @@ export const DICT_TYPE = {
SYSTEM_ERROR_CODE_TYPE: 'system_error_code_type',
SYSTEM_OAUTH2_GRANT_TYPE: 'system_oauth2_grant_type',
SYSTEM_MAIL_SEND_STATUS: 'system_mail_send_status',
SYSTEM_NOTIFY_TEMPLATE_TYPE: 'system_notify_template_type',
// ========== INFRA 模块 ==========
INFRA_BOOLEAN_STRING: 'infra_boolean_string',

@ -1,5 +1,6 @@
<template>
<div class="app-container">
<doc-alert title="公众号接入" url="https://doc.iocoder.cn/mp/account/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">

@ -26,6 +26,8 @@ SOFTWARE.
-->
<template>
<div class="app-container">
<doc-alert title="自动回复" url="https://doc.iocoder.cn/mp/auto-reply/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">

@ -27,6 +27,8 @@ SOFTWARE.
-->
<template>
<div class="app-container">
<doc-alert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">

@ -25,6 +25,8 @@ SOFTWARE.
-->
<template>
<div class="app-container">
<doc-alert title="公众号图文" url="https://doc.iocoder.cn/mp/article/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">

@ -27,6 +27,8 @@ SOFTWARE.
-->
<template>
<div class="app-container">
<doc-alert title="公众号素材" url="https://doc.iocoder.cn/mp/material/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">

@ -26,6 +26,8 @@ SOFTWARE.
-->
<template>
<div class="app-container">
<doc-alert title="公众号菜单" url="https://doc.iocoder.cn/mp/menu/" />
<!-- 搜索工作栏 -->
<el-form ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">

@ -1,5 +1,6 @@
<template>
<div class="app-container">
<doc-alert title="公众号消息" url="https://doc.iocoder.cn/mp/message/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">

@ -1,5 +1,7 @@
<template>
<div class="app-container">
<doc-alert title="公众号统计" url="https://doc.iocoder.cn/mp/statistics/" />
<!-- 搜索工作栏 -->
<el-form ref="queryForm" size="small" :inline="true" label-width="68px">
<el-form-item label="公众号" prop="accountId">

@ -1,5 +1,7 @@
<template>
<div class="app-container">
<doc-alert title="公众号标签" url="https://doc.iocoder.cn/mp/tag/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="公众号" prop="accountId">

@ -1,5 +1,6 @@
<template>
<div class="app-container">
<doc-alert title="公众号粉丝" url="https://doc.iocoder.cn/mp/user/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">

@ -140,7 +140,7 @@ export default {
//
list: [],
//
title: "",
title: "邮件发送日志详细",
//
open: false,
//

@ -0,0 +1,179 @@
<template>
<div class="app-container">
<doc-alert title="站内信配置" url="https://doc.iocoder.cn/notify/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="用户编号" prop="userId">
<el-input v-model="queryParams.userId" placeholder="请输入用户编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="用户类型" prop="userType">
<el-select v-model="queryParams.userType" placeholder="请选择用户类型" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.USER_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="模板编码" prop="templateCode">
<el-input v-model="queryParams.templateCode" placeholder="请输入模板编码" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="模版类型" prop="templateType">
<el-select v-model="queryParams.templateType" placeholder="请选择模版类型" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="用户编号" align="center" prop="userId" />
<el-table-column label="用户类型" align="center" prop="userType">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.USER_TYPE" :value="scope.row.userType" />
</template>
</el-table-column>
<el-table-column label="模板编码" align="center" prop="templateCode" />
<el-table-column label="发送人名称" align="center" prop="templateNickname" />
<el-table-column label="模版内容" align="center" prop="templateContent" />
<el-table-column label="模版类型" align="center" prop="templateType">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE" :value="scope.row.templateType" />
</template>
</el-table-column>
<el-table-column label="是否已读" align="center" prop="readStatus">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.readStatus" />
</template>
</el-table-column>
<el-table-column label="阅读时间" align="center" prop="readTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.readTime) }}</span>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(scope.row)"
v-hasPermi="['system:notify-message:query']">详细</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 站内信详细-->
<el-dialog :title="title" :visible.sync="open" width="700px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" label-width="160px">
<el-row>
<el-col :span="24">
<el-form-item label="日志主键:">{{ form.id }}</el-form-item>
<el-form-item label="发送时间:">{{ parseTime(form.createTime) }}</el-form-item>
<el-form-item label="用户编号:">{{ form.userId }}</el-form-item>
<el-form-item label="用户类型:">
<dict-tag :type="DICT_TYPE.USER_TYPE" :value="form.userType"/>
</el-form-item>
<el-form-item label="模板编号:">{{ form.templateId }}</el-form-item>
<el-form-item label="模板编码:">{{ form.templateCode }}</el-form-item>
<el-form-item label="模板类型:">
<dict-tag :type="DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE" :value="form.templateType" />
</el-form-item>
<el-form-item label="模版发送人名称:">{{ form.templateNickname }}</el-form-item>
<el-form-item label="邮件内容:">{{ form.templateContent }}</el-form-item>
<el-form-item label="模版参数:">{{ form.templateParams }}</el-form-item>
<el-form-item label="是否已读:">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="form.readStatus" />
</el-form-item>
<el-form-item label="阅读时间:">{{ parseTime(form.readTime) }}</el-form-item>
</el-col>
</el-row>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="open = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getNotifyMessagePage } from "@/api/system/notify/message";
export default {
name: "NotifyMessage",
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
list: [],
//
title: "站内信详细",
//
open: false,
//
queryParams: {
pageNo: 1,
pageSize: 10,
userId: null,
userType: null,
templateCode: null,
templateType: null,
createTime: [],
},
//
form: {},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
getNotifyMessagePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 详细按钮操作 */
handleView(row) {
this.open = true;
this.form = row;
}
}
};
</script>

@ -0,0 +1,139 @@
<template>
<div class="app-container">
<doc-alert title="站内信配置" url="https://doc.iocoder.cn/notify/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="是否已读" prop="readStatus">
<el-select v-model="queryParams.readStatus" placeholder="请选择状态" clearable>
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="发送时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleUpdateList"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleUpdateAll"></el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" ref="tables" :data="list">
<el-table-column type="selection" width="55" />
<el-table-column label="发送人" align="center" prop="templateNickname" width="120" />
<el-table-column label="发送时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="类型" align="center" prop="templateType" width="80">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE" :value="scope.row.templateType" />
</template>
</el-table-column>
<el-table-column label="内容" align="center" prop="templateContent" />
<el-table-column label="是否已读" align="center" prop="readStatus" width="80">
<template slot-scope="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.readStatus"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
<template slot-scope="scope">
<el-button v-show="!scope.row.readStatus" size="mini" type="text" icon="el-icon-check" @click="handleUpdateSingle(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import {getMyNotifyMessagePage, updateAllNotifyMessageRead, updateNotifyMessageRead} from "@/api/system/notify/message";
export default {
name: "myNotify",
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
list: [],
//
queryParams: {
pageNo: 1,
pageSize: 10,
readStatus: null,
createTime: []
},
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
getMyNotifyMessagePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
handleUpdateList() {
let list = this.$refs["tables"].selection;
if (list.length === 0) {
return;
}
this.handleUpdate(list.map(v => v.id))
},
handleUpdateSingle(row) {
this.handleUpdate([row.id])
},
handleUpdate(ids) {
updateNotifyMessageRead(ids).then(response => {
this.$modal.msgSuccess("标记已读成功!");
this.getList();
});
},
handleUpdateAll(){
updateAllNotifyMessageRead().then(response => {
this.$modal.msgSuccess("全部已读成功!");
this.getList();
});
}
}
}
</script>

@ -0,0 +1,340 @@
<template>
<div class="app-container">
<doc-alert title="站内信配置" url="https://doc.iocoder.cn/notify/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="模板名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入模板名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="模版编码" prop="code">
<el-input v-model="queryParams.code" placeholder="请输入模版编码" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery"></el-button>
<el-button icon="el-icon-refresh" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['system:notify-template:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="模板编码" align="center" prop="code" />
<el-table-column label="模板名称" align="center" prop="name" />
<el-table-column label="类型" align="center" prop="type">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE" :value="scope.row.type" />
</template>
</el-table-column>
<el-table-column label="发送人名称" align="center" prop="nickname" />
<el-table-column label="模板内容" align="center" prop="content" width="300" />
<el-table-column label="开启状态" align="center" prop="status">
<template slot-scope="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="150">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-share" @click="handleSendNotify(scope.row)"
v-hasPermi="['system:notify-template:send-notify']">测试</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['system:notify-template:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['system:notify-template:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
<el-form-item label="模版编码" prop="code">
<el-input v-model="form.code" placeholder="请输入模版编码" />
</el-form-item>
<el-form-item label="模板名称" prop="name">
<el-input v-model="form.name" placeholder="请输入模版名称" />
</el-form-item>
<el-form-item label="发件人名称" prop="nickname">
<el-input v-model="form.nickname" placeholder="请输入发件人名称" />
</el-form-item>
<el-form-item label="模板内容" prop="content">
<el-input type="textarea" v-model="form.content" placeholder="请输入模板内容" />
</el-form-item>
<el-form-item label="类型" prop="type">
<el-select v-model="form.type" placeholder="请选择类型">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)" />
</el-select>
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
<!-- 对话框(发送站内信) -->
<el-dialog title="发送站内信" :visible.sync="sendNotifyOpen" width="500px" append-to-body>
<el-form ref="sendNotifyForm" :model="sendNotifyForm" :rules="sendNotifyRules" label-width="140px">
<el-form-item label="模板内容" prop="content">
<el-input v-model="sendNotifyForm.content" type="textarea" placeholder="请输入模板内容" readonly />
</el-form-item>
<el-form-item label="接收人" prop="userId">
<el-select v-model="sendNotifyForm.userId" placeholder="请输入接收人" clearable style="width: 100%">
<el-option v-for="item in users" :key="parseInt(item.id)" :label="item.nickname" :value="parseInt(item.id)" />
</el-select>
</el-form-item>
<el-form-item v-for="param in sendNotifyForm.params" :key="param" :label="'参数 {' + param + '}'" :prop="'templateParams.' + param">
<el-input v-model="sendNotifyForm.templateParams[param]" :placeholder="'请输入 ' + param + ' 参数'" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitSendNotifyForm"> </el-button>
<el-button @click="cancelSendNotify"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { createNotifyTemplate, updateNotifyTemplate, deleteNotifyTemplate, getNotifyTemplate, getNotifyTemplatePage,
sendNotify } from "@/api/system/notify/template";
import {listSimpleUsers} from "@/api/system/user";
import {CommonStatusEnum} from "@/utils/constants";
export default {
name: "NotifyTemplate",
data() {
return {
//
loading: true,
//
showSearch: true,
//
total: 0,
//
list: [],
//
title: "",
//
open: false,
//
queryParams: {
pageNo: 1,
pageSize: 10,
status: null,
code: null,
title: null,
createTime: []
},
//
form: {},
//
rules: {
name: [{ required: true, message: "模板名称不能为空", trigger: "blur" }],
code: [{ required: true, message: "模版编码不能为空", trigger: "blur" }],
nickname: [{ required: true, message: "发件人名称不能为空", trigger: "blur" }],
content: [{ required: true, message: "模版内容不能为空", trigger: "blur" }],
type: [{ required: true, message: "类型不能为空", trigger: "change" }],
status: [{ required: true, message: "状态不能为空", trigger: "blur" }],
},
//
users: [],
//
sendNotifyOpen: false,
sendNotifyForm: {
params: [], //
},
sendNotifyRules: {
userId: [{ required: true, message: "接收人不能为空", trigger: "blur" }],
templateCode: [{ required: true, message: "模版编码不能为空", trigger: "blur" }],
templateParams: { }
}
};
},
created() {
this.getList();
//
listSimpleUsers().then(response => {
this.users = response.data;
})
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
//
getNotifyTemplatePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
code: undefined,
nickname: undefined,
content: undefined,
type: undefined,
params: undefined,
status: CommonStatusEnum.ENABLE,
remark: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加站内信模板";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getNotifyTemplate(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改站内信模板";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
//
if (this.form.id != null) {
updateNotifyTemplate(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
//
createNotifyTemplate(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除站内信模板编号为"' + id + '"的数据项?').then(function() {
return deleteNotifyTemplate(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 发送站内信按钮 */
handleSendNotify(row) {
this.resetSendNotify(row);
//
this.sendNotifyForm.content = row.content;
this.sendNotifyForm.params = row.params;
this.sendNotifyForm.templateCode = row.code;
this.sendNotifyForm.templateParams = row.params.reduce(function(obj, item) {
obj[item] = undefined;
return obj;
}, {});
// row rules
this.sendNotifyRules.templateParams = row.params.reduce(function(obj, item) {
obj[item] = { required: true, message: '参数 ' + item + " 不能为空", trigger: "change" };
return obj;
}, {});
//
this.sendNotifyOpen = true;
},
/** 重置发送站内信的表单 */
resetSendNotify() {
// row
this.sendNotifyForm = {
content: undefined,
params: undefined,
userId: undefined,
templateCode: undefined,
templateParams: {}
};
this.resetForm("sendNotifyForm");
},
/** 取消发送站内信 */
cancelSendNotify() {
this.sendNotifyOpen = false;
this.resetSendNotify();
},
/** 提交按钮 */
submitSendNotifyForm() {
this.$refs["sendNotifyForm"].validate(valid => {
if (!valid) {
return;
}
//
sendNotify(this.sendNotifyForm).then(response => {
this.$modal.msgSuccess("提交发送成功!发送结果,见发送日志编号:" + response.data);
this.sendNotifyOpen = false;
});
});
},
}
};
</script>

@ -1,5 +1,6 @@
<template>
<div class="app-container">
<doc-alert title="OAuth 2.0SSO 单点登录)" url="https://doc.iocoder.cn/oauth2/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">

@ -1,6 +1,8 @@
<template>
<div class="app-container">
<doc-alert title="OAuth 2.0SSO 单点登录)" url="https://doc.iocoder.cn/oauth2/" />
<doc-alert title="用户体系" url="https://doc.iocoder.cn/user-center/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="68px">
<el-form-item label="用户编号" prop="userId">

Loading…
Cancel
Save