diff --git a/README.md b/README.md index 6f13072b3..3d4b58d49 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ | | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | | 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 | | 🚀 | 邮件管理 | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台 | +| 🚀 | 站内信 | 系统内的消息通知,提供站内信模版、站内信消息 | | 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 | | ⭐️ | 登录日志 | 系统登录日志记录查询,包含登录异常 | | 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 | diff --git a/sql/mysql/ruoyi-vue-pro.sql b/sql/mysql/ruoyi-vue-pro.sql index 8d34f8c14..7cd287e83 100644 --- a/sql/mysql/ruoyi-vue-pro.sql +++ b/sql/mysql/ruoyi-vue-pro.sql @@ -11,7 +11,7 @@ Target Server Version : 80026 File Encoding : 65001 - Date: 27/01/2023 20:57:31 + Date: 29/01/2023 18:44:35 */ SET NAMES utf8mb4; @@ -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, '我是测试标题', '

哈哈哈哈123

', 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); diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java index 68e0ead10..7b07fa1f5 100644 --- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java +++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/enums/CommonStatusEnum.java @@ -1,8 +1,11 @@ package cn.iocoder.yudao.framework.common.enums; +import cn.iocoder.yudao.framework.common.core.IntArrayValuable; import lombok.AllArgsConstructor; import lombok.Getter; +import java.util.Arrays; + /** * 通用状态枚举 * @@ -10,11 +13,13 @@ import lombok.Getter; */ @Getter @AllArgsConstructor -public enum CommonStatusEnum { +public enum CommonStatusEnum implements IntArrayValuable { ENABLE(0, "开启"), DISABLE(1, "关闭"); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CommonStatusEnum::getStatus).toArray(); + /** * 状态值 */ @@ -24,4 +29,9 @@ public enum CommonStatusEnum { */ private final String name; + @Override + public int[] array() { + return ARRAYS; + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java index c5198f855..9661b7363 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java @@ -94,6 +94,19 @@ public class QueryWrapperX extends QueryWrapper { return this; } + public QueryWrapperX betweenIfPresent(String column, Object[] values) { + if (values!= null && values.length != 0 && values[0] != null && values[1] != null) { + return (QueryWrapperX) super.between(column, values[0], values[1]); + } + if (values!= null && values.length != 0 && values[0] != null) { + return (QueryWrapperX) ge(column, values[0]); + } + if (values!= null && values.length != 0 && values[1] != null) { + return (QueryWrapperX) le(column, values[1]); + } + return this; + } + // ========== 重写父类方法,方便链式调用 ========== @Override @@ -133,19 +146,19 @@ public class QueryWrapperX extends QueryWrapper { * * @return this */ - public QueryWrapperX limit1() { + public QueryWrapperX 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; } diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApi.java new file mode 100644 index 000000000..781a792ad --- /dev/null +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApi.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.system.api.notify; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; +import cn.iocoder.yudao.module.system.enums.ApiConstants; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; + +import javax.validation.Valid; + +@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = +@Api(tags = "RPC 服务 - 站内信发送") +public interface NotifyMessageSendApi { + + String PREFIX = ApiConstants.PREFIX + "/notify/send"; + + @PostMapping(PREFIX + "/send-single-admin") + @ApiOperation(value = "发送单条站内信给 Admin 用户") + CommonResult sendSingleMessageToAdmin(@Valid NotifySendSingleToUserReqDTO reqDTO); + + @PostMapping(PREFIX + "/send-single-member") + @ApiOperation(value = "发送单条站内信给 Member 用户") + CommonResult sendSingleMessageToMember(@Valid NotifySendSingleToUserReqDTO reqDTO); + +} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifySendSingleToUserReqDTO.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifySendSingleToUserReqDTO.java new file mode 100644 index 000000000..124b9c74a --- /dev/null +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/dto/NotifySendSingleToUserReqDTO.java @@ -0,0 +1,24 @@ +package cn.iocoder.yudao.module.system.api.notify.dto; + +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("RPC 服务 - 站内信发送给 Admin 或者 Member 用户 Request DTO") +@Data +public class NotifySendSingleToUserReqDTO { + + @ApiModelProperty(value = "用户编号", required = true, example = "1024") + @NotNull(message = "用户编号不能为空") + private Long userId; + + @ApiModelProperty(value = "站内信模板编号", required = true, example = "USER_SEND") + @NotEmpty(message = "站内信模板编号不能为空") + private String templateCode; + @ApiModelProperty(value = "邮件模板参数") + private Map templateParams; +} diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java index 5eea402f7..b108ca775 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java @@ -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, "模板参数({})缺失"); + } diff --git a/yudao-module-system/yudao-module-system-biz/pom.xml b/yudao-module-system/yudao-module-system-biz/pom.xml index d6f977809..dd80676e4 100644 --- a/yudao-module-system/yudao-module-system-biz/pom.xml +++ b/yudao-module-system/yudao-module-system-biz/pom.xml @@ -150,6 +150,11 @@ yudao-spring-boot-starter-captcha + + org.springframework.boot + spring-boot-starter-mail + + cn.iocoder.cloud diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/notify/notify/NotifyMessageSendApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/notify/notify/NotifyMessageSendApiImpl.java new file mode 100644 index 000000000..58fa1fea1 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/notify/notify/NotifyMessageSendApiImpl.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.system.api.notify.notify; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi; +import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO; +import cn.iocoder.yudao.module.system.service.notify.NotifySendService; +import org.apache.dubbo.config.annotation.DubboService; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.module.system.enums.ApiConstants.VERSION; + +@RestController // 提供 RESTful API 接口,给 Feign 调用 +@DubboService(version = VERSION) // 提供 Dubbo RPC 接口,给 Dubbo Consumer 调用 +@Validated +public class NotifyMessageSendApiImpl implements NotifyMessageSendApi { + + @Resource + private NotifySendService notifySendService; + + @Override + public CommonResult sendSingleMessageToAdmin(NotifySendSingleToUserReqDTO reqDTO) { + return success(notifySendService.sendSingleNotifyToAdmin(reqDTO.getUserId(), + reqDTO.getTemplateCode(), reqDTO.getTemplateParams())); + } + + @Override + public CommonResult sendSingleMessageToMember(NotifySendSingleToUserReqDTO reqDTO) { + return success(notifySendService.sendSingleNotifyToMember(reqDTO.getUserId(), + reqDTO.getTemplateCode(), reqDTO.getTemplateParams())); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java new file mode 100644 index 000000000..51d236110 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyMessageController.java @@ -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 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> getNotifyMessagePage(@Valid NotifyMessagePageReqVO pageVO) { + PageResult pageResult = notifyMessageService.getNotifyMessagePage(pageVO); + return success(NotifyMessageConvert.INSTANCE.convertPage(pageResult)); + } + + // ========== 查看自己的站内信 ========== + + @GetMapping("/my-page") + @ApiOperation("获得我的站内信分页") + public CommonResult> getMyMyNotifyMessagePage(@Valid NotifyMessageMyPageReqVO pageVO) { + PageResult 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 updateNotifyMessageRead(@RequestParam("ids") List ids) { + notifyMessageService.updateNotifyMessageRead(ids, getLoginUserId(), UserTypeEnum.ADMIN.getValue()); + return success(Boolean.TRUE); + } + + @PutMapping("/update-all-read") + @ApiOperation("标记所有站内信为已读") + public CommonResult 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> getUnreadNotifyMessageList( + @RequestParam(name = "size", defaultValue = "10") Integer size) { + List list = notifyMessageService.getUnreadNotifyMessageList( + getLoginUserId(), UserTypeEnum.ADMIN.getValue(), size); + return success(NotifyMessageConvert.INSTANCE.convertList(list)); + } + + @GetMapping("/get-unread-count") + @ApiOperation("获得当前用户的未读站内信数量") + public CommonResult getUnreadNotifyMessageCount() { + return success(notifyMessageService.getUnreadNotifyMessageCount(getLoginUserId(), UserTypeEnum.ADMIN.getValue())); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyTemplateController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyTemplateController.java new file mode 100644 index 000000000..c90145dcd --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyTemplateController.java @@ -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 createNotifyTemplate(@Valid @RequestBody NotifyTemplateCreateReqVO createReqVO) { + return success(notifyTemplateService.createNotifyTemplate(createReqVO)); + } + + @PutMapping("/update") + @ApiOperation("更新站内信模版") + @PreAuthorize("@ss.hasPermission('system:notify-template:update')") + public CommonResult 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 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 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> getNotifyTemplatePage(@Valid NotifyTemplatePageReqVO pageVO) { + PageResult pageResult = notifyTemplateService.getNotifyTemplatePage(pageVO); + return success(NotifyTemplateConvert.INSTANCE.convertPage(pageResult)); + } + + @PostMapping("/send-notify") + @ApiOperation("发送站内信") + @PreAuthorize("@ss.hasPermission('system:notify-template:send-notify')") + public CommonResult sendNotify(@Valid @RequestBody NotifyTemplateSendReqVO sendReqVO) { + return success(notifySendService.sendSingleNotifyToAdmin(sendReqVO.getUserId(), + sendReqVO.getTemplateCode(), sendReqVO.getTemplateParams())); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageBaseVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageBaseVO.java new file mode 100644 index 000000000..674afcd8e --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageBaseVO.java @@ -0,0 +1,60 @@ +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.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 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; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageMyPageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageMyPageReqVO.java new file mode 100644 index 000000000..8d3eac162 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageMyPageReqVO.java @@ -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; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessagePageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessagePageReqVO.java new file mode 100644 index 000000000..9e7c9a1c3 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessagePageReqVO.java @@ -0,0 +1,37 @@ +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 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; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageRespVO.java new file mode 100644 index 000000000..44e555f25 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/message/NotifyMessageRespVO.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.controller.admin.notify.vo.message; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; + +@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; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateBaseVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateBaseVO.java new file mode 100644 index 000000000..118f55baf --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateBaseVO.java @@ -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; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateCreateReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateCreateReqVO.java new file mode 100644 index 000000000..6cb91efa0 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateCreateReqVO.java @@ -0,0 +1,13 @@ +package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template; + +import io.swagger.annotations.ApiModel; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@ApiModel("管理后台 - 站内信模版创建 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class NotifyTemplateCreateReqVO extends NotifyTemplateBaseVO { +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplatePageReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplatePageReqVO.java new file mode 100644 index 000000000..34965a792 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplatePageReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.system.controller.admin.notify.vo.template; + +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 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; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateRespVO.java new file mode 100644 index 000000000..749d1a191 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateRespVO.java @@ -0,0 +1,27 @@ +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 lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Date; +import java.util.List; + +@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 params; + + @ApiModelProperty(value = "创建时间", required = true) + private Date createTime; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSendReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSendReqVO.java new file mode 100644 index 000000000..e078fd845 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSendReqVO.java @@ -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 templateParams; +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateUpdateReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateUpdateReqVO.java new file mode 100644 index 000000000..691807141 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateUpdateReqVO.java @@ -0,0 +1,21 @@ +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 lombok.EqualsAndHashCode; +import lombok.ToString; + +import javax.validation.constraints.NotNull; + +@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; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/notify/NotifyMessageConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/notify/NotifyMessageConvert.java new file mode 100644 index 000000000..4a393d30c --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/notify/NotifyMessageConvert.java @@ -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 convertList(List list); + + PageResult convertPage(PageResult page); + + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/notify/NotifyTemplateConvert.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/notify/NotifyTemplateConvert.java new file mode 100644 index 000000000..e83d51d5b --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/notify/NotifyTemplateConvert.java @@ -0,0 +1,33 @@ +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.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 cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 站内信模版 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 convertList(List list); + + PageResult convertPage(PageResult page); + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyMessageDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyMessageDO.java new file mode 100644 index 000000000..501a77cb5 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyMessageDO.java @@ -0,0 +1,99 @@ +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 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.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 templateParams; + + // ========= 读取相关字段 ========= + + /** + * 是否已读 + */ + private Boolean readStatus; + /** + * 阅读时间 + */ + private LocalDateTime readTime; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyTemplateDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyTemplateDO.java new file mode 100644 index 000000000..1bce809a1 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/notify/NotifyTemplateDO.java @@ -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 params; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 备注 + */ + private String remark; + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java new file mode 100644 index 000000000..e9ce6d719 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java @@ -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 { + + default PageResult selectPage(NotifyMessagePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .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 selectPage(NotifyMessageMyPageReqVO reqVO, Long userId, Integer userType) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .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 ids, Long userId, Integer userType) { + return update(new NotifyMessageDO().setReadStatus(true).setReadTime(LocalDateTime.now()), + new LambdaQueryWrapperX() + .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() + .eq(NotifyMessageDO::getUserId, userId) + .eq(NotifyMessageDO::getUserType, userType) + .eq(NotifyMessageDO::getReadStatus, false)); + } + + default List selectUnreadListByUserIdAndUserType(Long userId, Integer userType, Integer size) { + return selectList(new QueryWrapperX() // 由于要使用 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() + .eq(NotifyMessageDO::getReadStatus, false) + .eq(NotifyMessageDO::getUserId, userId) + .eq(NotifyMessageDO::getUserType, userType)); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyTemplateMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyTemplateMapper.java new file mode 100644 index 000000000..1fcb8ee0d --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyTemplateMapper.java @@ -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 { + + default NotifyTemplateDO selectByCode(String code) { + return selectOne(NotifyTemplateDO::getCode, code); + } + + default PageResult selectPage(NotifyTemplatePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(NotifyTemplateDO::getCode, reqVO.getCode()) + .likeIfPresent(NotifyTemplateDO::getName, reqVO.getName()) + .eqIfPresent(NotifyTemplateDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(NotifyTemplateDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(NotifyTemplateDO::getId)); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsCodeMapper.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsCodeMapper.java index 854eb7faa..599c1abef 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsCodeMapper.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsCodeMapper.java @@ -22,7 +22,7 @@ public interface SmsCodeMapper extends BaseMapperX { .eqIfPresent("scene", scene) .eqIfPresent("code", code) .orderByDesc("id") - .limit1()); + .limitN(1)); } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/notify/NotifyTemplateRefreshConsumer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/notify/NotifyTemplateRefreshConsumer.java new file mode 100644 index 000000000..757a6f251 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/consumer/notify/NotifyTemplateRefreshConsumer.java @@ -0,0 +1,29 @@ +package cn.iocoder.yudao.module.system.mq.consumer.notify; + +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.context.event.EventListener; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * 针对 {@link NotifyTemplateRefreshMessage} 的消费者 + * + * @author xrcoder + */ +@Component +@Slf4j +public class NotifyTemplateRefreshConsumer { + + @Resource + private NotifyTemplateService notifyTemplateService; + + @EventListener + public void onMessage(NotifyTemplateRefreshMessage message) { + log.info("[onMessage][收到 NotifyTemplate 刷新消息]"); + notifyTemplateService.initLocalCache(); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/notify/NotifyTemplateRefreshMessage.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/notify/NotifyTemplateRefreshMessage.java new file mode 100644 index 000000000..e220708cb --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/message/notify/NotifyTemplateRefreshMessage.java @@ -0,0 +1,23 @@ +package cn.iocoder.yudao.module.system.mq.message.notify; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springframework.cloud.bus.event.RemoteApplicationEvent; + +/** + * 站内信模板的数据刷新 Message + * + * @author xrcoder + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class NotifyTemplateRefreshMessage extends RemoteApplicationEvent { + + public NotifyTemplateRefreshMessage() { + } + + public NotifyTemplateRefreshMessage(Object source, String originService, String destinationService) { + super(source, originService, DEFAULT_DESTINATION_FACTORY.getDestination(destinationService)); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/notify/NotifyProducer.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/notify/NotifyProducer.java new file mode 100644 index 000000000..c8c2f6f86 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/mq/producer/notify/NotifyProducer.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.system.mq.producer.notify; + +import cn.iocoder.yudao.framework.mq.core.bus.AbstractBusProducer; +import cn.iocoder.yudao.module.system.mq.message.notify.NotifyTemplateRefreshMessage; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * Notify 站内信相关消息的 Producer + * + * @author xrcoder + * @since 2022-08-06 + */ +@Slf4j +@Component +public class NotifyProducer extends AbstractBusProducer { + + /** + * 发送 {@link NotifyTemplateRefreshMessage} 消息 + */ + public void sendNotifyTemplateRefreshMessage() { + publishEvent(new NotifyTemplateRefreshMessage(this, getBusId(), selfDestinationService())); + } + + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageService.java new file mode 100644 index 000000000..b06aef322 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageService.java @@ -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 templateParams); + + /** + * 获得站内信分页 + * + * @param pageReqVO 分页查询 + * @return 站内信分页 + */ + PageResult getNotifyMessagePage(NotifyMessagePageReqVO pageReqVO); + + /** + * 获得【我的】站内信分页 + * + * @param pageReqVO 分页查询 + * @param userId 用户编号 + * @param userType 用户类型 + * @return 站内信分页 + */ + PageResult getMyMyNotifyMessagePage(NotifyMessageMyPageReqVO pageReqVO, Long userId, Integer userType); + + /** + * 获得站内信 + * + * @param id 编号 + * @return 站内信 + */ + NotifyMessageDO getNotifyMessage(Long id); + + /** + * 获得【我的】未读站内信列表 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param size 数量 + * @return 站内信列表 + */ + List 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 ids, Long userId, Integer userType); + + /** + * 标记所有站内信为已读 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @return 更新到的条数 + */ + int updateAllNotifyMessageRead(Long userId, Integer userType); + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImpl.java new file mode 100644 index 000000000..1ac4c0488 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImpl.java @@ -0,0 +1,75 @@ +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; + +/** + * 站内信 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 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 getNotifyMessagePage(NotifyMessagePageReqVO pageReqVO) { + return notifyMessageMapper.selectPage(pageReqVO); + } + + @Override + public PageResult 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 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 ids, Long userId, Integer userType) { + return notifyMessageMapper.updateListRead(ids, userId, userType); + } + + @Override + public int updateAllNotifyMessageRead(Long userId, Integer userType) { + return notifyMessageMapper.updateListRead(userId, userType); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendService.java new file mode 100644 index 000000000..f4a8d4f4c --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendService.java @@ -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 templateParams); + /** + * 发送单条站内信给用户 APP 的用户 + * + * 在 mobile 为空时,使用 userId 加载对应会员的手机号 + * + * @param userId 用户编号 + * @param templateCode 站内信模板编号 + * @param templateParams 站内信模板参数 + * @return 发送日志编号 + */ + Long sendSingleNotifyToMember(Long userId, + String templateCode, Map templateParams); + + /** + * 发送单条站内信给用户 + * + * @param userId 用户编号 + * @param userType 用户类型 + * @param templateCode 站内信模板编号 + * @param templateParams 站内信模板参数 + * @return 发送日志编号 + */ + Long sendSingleNotify( Long userId, Integer userType, + String templateCode, Map templateParams); + + default void sendBatchNotify(List mobiles, List userIds, Integer userType, + String templateCode, Map templateParams) { + throw new UnsupportedOperationException("暂时不支持该操作,感兴趣可以实现该功能哟!"); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImpl.java new file mode 100644 index 000000000..39e685fd5 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImpl.java @@ -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 templateParams) { + return sendSingleNotify(userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams); + } + + @Override + public Long sendSingleNotifyToMember(Long userId, String templateCode, Map templateParams) { + return sendSingleNotify(userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams); + } + + @Override + public Long sendSingleNotify(Long userId, Integer userType, String templateCode, Map 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 templateParams) { + template.getParams().forEach(key -> { + Object value = templateParams.get(key); + if (value == null) { + throw exception(NOTIFY_SEND_TEMPLATE_PARAM_MISS, key); + } + }); + } +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java new file mode 100644 index 000000000..260159b79 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java @@ -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 getNotifyTemplatePage(NotifyTemplatePageReqVO pageReqVO); + + /** + * 格式化站内信内容 + * + * @param content 站内信模板的内容 + * @param params 站内信内容的参数 + * @return 格式化后的内容 + */ + String formatNotifyTemplateContent(String content, Map params); + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java new file mode 100644 index 000000000..5ee8b4bbc --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java @@ -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()} + *

+ * 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 + */ + private volatile Map notifyTemplateCache; + + /** + * 初始化站内信模板的本地缓存 + */ + @Override + @PostConstruct + public void initLocalCache() { + // 第一步:查询数据 + List 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 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 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 params) { + return StrUtil.format(content, params); + } +} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImplTest.java new file mode 100644 index 000000000..1087918cc --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyMessageServiceImplTest.java @@ -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 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 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 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 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 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 randomTemplateParams() { + return MapUtil.builder().put(randomString(), randomString()) + .put(randomString(), randomString()).build(); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImplTest.java new file mode 100644 index 000000000..ddc214443 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImplTest.java @@ -0,0 +1,120 @@ +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.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.NOTICE_NOT_FOUND; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.NOTIFY_SEND_TEMPLATE_PARAM_MISS; +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 templateParams = MapUtil.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 templateParams = MapUtil.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 templateParams = new HashMap<>(); + // mock 方法 + + // 调用,并断言异常 + assertServiceException(() -> notifySendService.checkTemplateParams(template, templateParams), + NOTIFY_SEND_TEMPLATE_PARAM_MISS, "code"); + } + + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java new file mode 100644 index 000000000..5a6f5a508 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java @@ -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 pageResult = notifyTemplateService.getNotifyTemplatePage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbNotifyTemplate, pageResult.getList().get(0)); + } + +} diff --git a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql index 1fb4cbaf9..1be69f28b 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql +++ b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/clean.sql @@ -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"; diff --git a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql index 0acd892c3..b00c9168e 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql +++ b/yudao-module-system/yudao-module-system-biz/src/test/resources/sql/create_tables.sql @@ -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 '站内信消息表';