diff --git a/sql/mysql5/youlai_boot.sql b/sql/mysql5/youlai_boot.sql index 002f8afe..0d542dcb 100644 --- a/sql/mysql5/youlai_boot.sql +++ b/sql/mysql5/youlai_boot.sql @@ -85,7 +85,8 @@ CREATE TABLE `sys_dict` ( -- ---------------------------- -- Records of sys_dict -- ---------------------------- -INSERT INTO `sys_dict` VALUES (1, '性别', 'gender', 1, NULL, '2019-12-06 19:03:32', '2024-06-22 21:14:47', 0); +INSERT INTO `sys_dict` VALUES (1,'性别','gender',1,NULL,'2019-12-06 19:03:32','2024-06-22 21:14:47',0); +INSERT INTO `sys_dict` VALUES (2,'通知类型','notice_type',1,NULL,'2024-09-01 17:23:48','2024-09-01 17:23:54',0); -- ---------------------------- -- Table structure for sys_dict_item @@ -110,6 +111,8 @@ CREATE TABLE `sys_dict_item` ( INSERT INTO `sys_dict_item` VALUES (1, 1, '男', '1', 1, 1, NULL, '2019-05-05 13:07:52', '2022-06-12 23:20:39'); INSERT INTO `sys_dict_item` VALUES (2, 1, '女', '2', 1, 2, NULL, '2019-04-19 11:33:00', '2019-07-02 14:23:05'); INSERT INTO `sys_dict_item` VALUES (3, 1, '保密', '0', 1, 3, NULL, '2020-10-17 08:09:31', '2020-10-17 08:09:31'); +INSERT INTO `sys_dict_item` VALUES (4, 2,'系统通知','1',1, 1, NULL,'2020-10-17 08:09:31', '2020-10-17 08:09:31'); +INSERT INTO `sys_dict_item` VALUES (5, 2,'通知消息','2',1, 2, NULL,'2020-10-17 08:09:31', '2020-10-17 08:09:31'); -- ---------------------------- -- Table structure for sys_log @@ -158,6 +161,17 @@ CREATE TABLE `sys_menu` ( `params` text NULL COMMENT '路由参数', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 117 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '菜单管理' ROW_FORMAT = DYNAMIC; + `always_show` tinyint NULL DEFAULT 0 COMMENT '【目录】只有一个子路由是否始终显示(1-是 0-否)', + `keep_alive` tinyint NULL DEFAULT 0 COMMENT '【菜单】是否开启页面缓存(1-是 0-否)', + `visible` tinyint(1) NOT NULL DEFAULT 1 COMMENT '显示状态(1-显示 0-隐藏)', + `sort` int NULL DEFAULT 0 COMMENT '排序', + `icon` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '菜单图标', + `redirect` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '跳转路径', + `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间', + `update_time` datetime NULL DEFAULT NULL COMMENT '更新时间', + `params` text NULL COMMENT '路由参数', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 117 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '菜单管理' ROW_FORMAT = DYNAMIC; -- ---------------------------- -- Records of sys_menu @@ -217,6 +231,23 @@ INSERT INTO `sys_menu` VALUES (112, 110, '0,110', '参数(type=2)', 1, NULL, 'ro INSERT INTO `sys_menu` VALUES (117, 1, '0,1', '系统日志', 1, 'Log', 'log', 'system/log/index', NULL, 0, 1, 1, 6, 'document', NULL, '2024-06-28 07:43:16', '2024-06-28 07:43:16', NULL); INSERT INTO `sys_menu` VALUES (118, 0, '0', '系统工具', 2, NULL, '/codegen', 'Layout', NULL, 0, 1, 1, 2, 'menu', NULL, '2024-07-13 08:41:07', '2024-07-13 08:41:07', NULL); INSERT INTO `sys_menu` VALUES (119, 118, '0,118', '代码生成', 1, 'Codegen', 'codegen', 'codegen/index', NULL, 0, 1, 1, 1, 'code', NULL, '2024-07-13 08:44:51', '2024-07-13 08:44:51', NULL); +INSERT INTO `sys_menu` VALUES (118, 0, '0', '系统工具', 2, NULL, '/codegen', 'Layout', NULL, 0, 1, 1, 2, 'menu', NULL, '2024-07-13 08:41:07', '2024-07-13 08:41:07', NULL); +INSERT INTO `sys_menu` VALUES (119, 118, '0,118', '代码生成', 1, 'Codegen', 'codegen', 'codegen/index', NULL, 0, 1, 1, 1, 'code', NULL, '2024-07-13 08:44:51', '2024-07-13 08:44:51', NULL); +INSERT INTO `sys_menu` VALUES (120,1,'0,1','系统配置',1,'Config','config','system/config/index',NULL,0,1,1,7,'setting',NULL,'2024-07-30 16:29:24','2024-07-30 16:29:32',NULL); +INSERT INTO `sys_menu` VALUES (121,120,'0,1,120','查询系统配置',4,NULL,'',NULL,'sys:config:query',0,1,1,1,'',NULL,'2024-07-30 16:29:54','2024-07-30 16:29:54',NULL); +INSERT INTO `sys_menu` VALUES (122,120,'0,1,120','新增系统配置',4,NULL,'',NULL,'sys:config:add',0,1,1,2,'',NULL,'2024-07-30 16:30:12','2024-07-30 16:30:48',NULL); +INSERT INTO `sys_menu` VALUES (123,120,'0,1,120','修改系统配置',4,NULL,'',NULL,'sys:config:update',0,1,1,3,'',NULL,'2024-07-30 16:30:31','2024-07-30 16:30:31',NULL); +INSERT INTO `sys_menu` VALUES (124,120,'0,1,120','删除系统配置',4,NULL,'',NULL,'sys:config:delete',0,1,1,4,'',NULL,'2024-07-30 16:31:07','2024-07-30 16:31:07',NULL); +INSERT INTO `sys_menu` VALUES (125,120,'0,1,120','刷新系统配置',4,NULL,'',NULL,'sys:config:refresh',0,1,1,5,'',NULL,'2024-07-30 16:31:25','2024-07-30 16:31:25',NULL); +INSERT INTO `sys_menu` VALUES (126,1,'0,1','通知公告',1,'Notice','notice','system/notice/index',NULL,NULL,NULL,1,9,'',NULL,'2024-08-24 13:39:03','2024-08-24 13:39:03',NULL); +INSERT INTO `sys_menu` VALUES (127,126,'0,1,132','查询',4,NULL,'',NULL,'system:notice:query',NULL,NULL,1,1,'',NULL,'2024-08-24 13:39:03','2024-08-24 13:39:03',NULL); +INSERT INTO `sys_menu` VALUES (128,126,'0,1,133','新增',4,NULL,'',NULL,'system:notice:add',NULL,NULL,1,2,'',NULL,'2024-08-24 13:39:03','2024-08-24 13:39:03',NULL); +INSERT INTO `sys_menu` VALUES (129,126,'0,1,134','编辑',4,NULL,'',NULL,'system:notice:edit',NULL,NULL,1,3,'',NULL,'2024-08-24 13:39:03','2024-08-24 13:39:03',NULL); +INSERT INTO `sys_menu` VALUES (130,126,'0,1,135','删除',4,NULL,'',NULL,'system:notice:delete',NULL,NULL,1,4,'',NULL,'2024-08-24 13:39:03','2024-08-24 13:39:03',NULL); +INSERT INTO `sys_menu` VALUES (131,0,'0','消息中心',2,NULL,'/notice','Layout',NULL,1,1,1,13,'el-icon-Message',NULL,'2024-08-31 21:16:06','2024-08-31 21:16:41',NULL); +INSERT INTO `sys_menu` VALUES (132,131,'0,136','我的消息',1,'MyNotice','notice','notice/index',NULL,0,1,1,1,'',NULL,'2024-08-31 21:17:36','2024-09-12 11:28:42',NULL); +INSERT INTO `sys_menu` VALUES (133,132,'0,1,131','发布',4,NULL,'',NULL,'system:notice:release',0,1,1,5,'',NULL,'2024-09-01 23:53:52','2024-09-01 23:53:52',NULL); +INSERT INTO `sys_menu` VALUES (134,132,'0,1,131','撤回',4,NULL,'',NULL,'system:notice:recall',0,1,1,6,'',NULL,'2024-09-01 23:54:16','2024-09-01 23:54:16',NULL); -- ---------------------------- -- Table structure for sys_message @@ -344,6 +375,21 @@ INSERT INTO `sys_role_menu` VALUES (2, 116); INSERT INTO `sys_role_menu` VALUES (2, 117); INSERT INTO `sys_role_menu` VALUES (2, 118); INSERT INTO `sys_role_menu` VALUES (2, 119); +INSERT INTO `sys_role_menu` VALUES (2, 120); +INSERT INTO `sys_role_menu` VALUES (2, 121); +INSERT INTO `sys_role_menu` VALUES (2, 122); +INSERT INTO `sys_role_menu` VALUES (2, 123); +INSERT INTO `sys_role_menu` VALUES (2, 124); +INSERT INTO `sys_role_menu` VALUES (2, 125); +INSERT INTO `sys_role_menu` VALUES (2, 126); +INSERT INTO `sys_role_menu` VALUES (2, 127); +INSERT INTO `sys_role_menu` VALUES (2, 128); +INSERT INTO `sys_role_menu` VALUES (2, 129); +INSERT INTO `sys_role_menu` VALUES (2, 130); +INSERT INTO `sys_role_menu` VALUES (2, 131); +INSERT INTO `sys_role_menu` VALUES (2, 132); +INSERT INTO `sys_role_menu` VALUES (2, 133); +INSERT INTO `sys_role_menu` VALUES (2, 134); -- ---------------------------- -- Table structure for sys_user -- ---------------------------- @@ -465,5 +511,33 @@ CREATE TABLE `gen_field_config` ( KEY `config_id` (`config_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci COMMENT='代码生成字段配置表'; +CREATE TABLE `sys_notice` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `title` VARCHAR(50) DEFAULT NULL COMMENT '通知标题', + `content` TEXT COMMENT '通知内容', + `notice_type` INT NOT NULL COMMENT '通知类型', + `release_by` BIGINT DEFAULT NULL COMMENT '发布人', + `priority` TINYINT NOT NULL COMMENT '优先级(0-低 1-中 2-高)', + `tar_type` TINYINT NOT NULL COMMENT '目标类型(0-全体 1-指定)', + `tar_ids` TEXT COMMENT '目标人id', + `release_status` TINYINT NOT NULL COMMENT '发布状态(0-未发布 1已发布 2已撤回)', + `release_time` DATETIME DEFAULT NULL COMMENT '发布时间', + `recall_time` DATETIME DEFAULT NULL COMMENT '撤回时间', + `create_by` BIGINT NOT NULL COMMENT '创建人ID', + `create_time` DATETIME NOT NULL COMMENT '创建时间', + `update_by` BIGINT DEFAULT NULL COMMENT '更新人ID', + `update_time` DATETIME DEFAULT NULL COMMENT '更新时间', + `is_deleted` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除标识(0-未删除 1-已删除)', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=INNODB COMMENT='通知公告'; + +CREATE TABLE `sys_notice_status` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'id', + `notice_id` BIGINT NOT NULL COMMENT '公共通知id', + `user_id` INT NOT NULL COMMENT '用户id', + `read_status` BIGINT DEFAULT NULL COMMENT '读取状态,0未读,1已读取', + `read_time` DATETIME DEFAULT NULL COMMENT '用户阅读时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=INNODB COMMENT='用户公告状态表'; SET FOREIGN_KEY_CHECKS = 1; diff --git a/sql/mysql8/youlai_boot.sql b/sql/mysql8/youlai_boot.sql index 2cfdc939..370ff92f 100644 --- a/sql/mysql8/youlai_boot.sql +++ b/sql/mysql8/youlai_boot.sql @@ -87,6 +87,7 @@ CREATE TABLE `sys_dict` ( -- Records of sys_dict -- ---------------------------- INSERT INTO `sys_dict` VALUES (1, '性别', 'gender', 1, NULL, '2019-12-06 19:03:32', '2024-06-22 21:14:47', 0); +INSERT INTO `sys_dict` VALUES (2,'通知类型','notice_type',1,NULL,'2024-09-01 17:23:48','2024-09-01 17:23:54',0); -- ---------------------------- -- Table structure for sys_dict_item @@ -111,6 +112,8 @@ CREATE TABLE `sys_dict_item` ( INSERT INTO `sys_dict_item` VALUES (1, 1, '男', '1', 1, 1, NULL, '2019-05-05 13:07:52', '2022-06-12 23:20:39'); INSERT INTO `sys_dict_item` VALUES (2, 1, '女', '2', 1, 2, NULL, '2019-04-19 11:33:00', '2019-07-02 14:23:05'); INSERT INTO `sys_dict_item` VALUES (3, 1, '保密', '0', 1, 3, NULL, '2020-10-17 08:09:31', '2020-10-17 08:09:31'); +INSERT INTO `sys_dict_item` VALUES (4, 2,'系统通知','1',1, 1, NULL,'2020-10-17 08:09:31', '2020-10-17 08:09:31'); +INSERT INTO `sys_dict_item` VALUES (5, 2,'通知消息','2',1, 2, NULL,'2020-10-17 08:09:31', '2020-10-17 08:09:31'); -- ---------------------------- -- Table structure for sys_log @@ -218,6 +221,21 @@ INSERT INTO `sys_menu` VALUES (112, 110, '0,110', '参数(type=2)', 1, NULL, 'ro INSERT INTO `sys_menu` VALUES (117, 1, '0,1', '系统日志', 1, 'Log', 'log', 'system/log/index', NULL, 0, 1, 1, 6, 'document', NULL, '2024-06-28 07:43:16', '2024-06-28 07:43:16', NULL); INSERT INTO `sys_menu` VALUES (118, 0, '0', '系统工具', 2, NULL, '/codegen', 'Layout', NULL, 0, 1, 1, 2, 'menu', NULL, '2024-07-13 08:41:07', '2024-07-13 08:41:07', NULL); INSERT INTO `sys_menu` VALUES (119, 118, '0,118', '代码生成', 1, 'Codegen', 'codegen', 'codegen/index', NULL, 0, 1, 1, 1, 'code', NULL, '2024-07-13 08:44:51', '2024-07-13 08:44:51', NULL); +INSERT INTO `sys_menu` VALUES (120,1,'0,1','系统配置',1,'Config','config','system/config/index',NULL,0,1,1,7,'setting',NULL,'2024-07-30 16:29:24','2024-07-30 16:29:32',NULL); +INSERT INTO `sys_menu` VALUES (121,120,'0,1,120','查询系统配置',4,NULL,'',NULL,'sys:config:query',0,1,1,1,'',NULL,'2024-07-30 16:29:54','2024-07-30 16:29:54',NULL); +INSERT INTO `sys_menu` VALUES (122,120,'0,1,120','新增系统配置',4,NULL,'',NULL,'sys:config:add',0,1,1,2,'',NULL,'2024-07-30 16:30:12','2024-07-30 16:30:48',NULL); +INSERT INTO `sys_menu` VALUES (123,120,'0,1,120','修改系统配置',4,NULL,'',NULL,'sys:config:update',0,1,1,3,'',NULL,'2024-07-30 16:30:31','2024-07-30 16:30:31',NULL); +INSERT INTO `sys_menu` VALUES (124,120,'0,1,120','删除系统配置',4,NULL,'',NULL,'sys:config:delete',0,1,1,4,'',NULL,'2024-07-30 16:31:07','2024-07-30 16:31:07',NULL); +INSERT INTO `sys_menu` VALUES (125,120,'0,1,120','刷新系统配置',4,NULL,'',NULL,'sys:config:refresh',0,1,1,5,'',NULL,'2024-07-30 16:31:25','2024-07-30 16:31:25',NULL); +INSERT INTO `sys_menu` VALUES (126,1,'0,1','通知公告',1,'Notice','notice','system/notice/index',NULL,NULL,NULL,1,9,'',NULL,'2024-08-24 13:39:03','2024-08-24 13:39:03',NULL); +INSERT INTO `sys_menu` VALUES (127,126,'0,1,132','查询',4,NULL,'',NULL,'system:notice:query',NULL,NULL,1,1,'',NULL,'2024-08-24 13:39:03','2024-08-24 13:39:03',NULL); +INSERT INTO `sys_menu` VALUES (128,126,'0,1,133','新增',4,NULL,'',NULL,'system:notice:add',NULL,NULL,1,2,'',NULL,'2024-08-24 13:39:03','2024-08-24 13:39:03',NULL); +INSERT INTO `sys_menu` VALUES (129,126,'0,1,134','编辑',4,NULL,'',NULL,'system:notice:edit',NULL,NULL,1,3,'',NULL,'2024-08-24 13:39:03','2024-08-24 13:39:03',NULL); +INSERT INTO `sys_menu` VALUES (130,126,'0,1,135','删除',4,NULL,'',NULL,'system:notice:delete',NULL,NULL,1,4,'',NULL,'2024-08-24 13:39:03','2024-08-24 13:39:03',NULL); +INSERT INTO `sys_menu` VALUES (131,0,'0','消息中心',2,NULL,'/notice','Layout',NULL,1,1,1,13,'el-icon-Message',NULL,'2024-08-31 21:16:06','2024-08-31 21:16:41',NULL); +INSERT INTO `sys_menu` VALUES (132,131,'0,136','我的消息',1,'MyNotice','notice','notice/index',NULL,0,1,1,1,'',NULL,'2024-08-31 21:17:36','2024-09-12 11:28:42',NULL); +INSERT INTO `sys_menu` VALUES (133,132,'0,1,131','发布',4,NULL,'',NULL,'system:notice:release',0,1,1,5,'',NULL,'2024-09-01 23:53:52','2024-09-01 23:53:52',NULL); +INSERT INTO `sys_menu` VALUES (134,132,'0,1,131','撤回',4,NULL,'',NULL,'system:notice:recall',0,1,1,6,'',NULL,'2024-09-01 23:54:16','2024-09-01 23:54:16',NULL); -- ---------------------------- -- Table structure for sys_message @@ -345,6 +363,21 @@ INSERT INTO `sys_role_menu` VALUES (2, 116); INSERT INTO `sys_role_menu` VALUES (2, 117); INSERT INTO `sys_role_menu` VALUES (2, 118); INSERT INTO `sys_role_menu` VALUES (2, 119); +INSERT INTO `sys_role_menu` VALUES (2, 120); +INSERT INTO `sys_role_menu` VALUES (2, 121); +INSERT INTO `sys_role_menu` VALUES (2, 122); +INSERT INTO `sys_role_menu` VALUES (2, 123); +INSERT INTO `sys_role_menu` VALUES (2, 124); +INSERT INTO `sys_role_menu` VALUES (2, 125); +INSERT INTO `sys_role_menu` VALUES (2, 126); +INSERT INTO `sys_role_menu` VALUES (2, 127); +INSERT INTO `sys_role_menu` VALUES (2, 128); +INSERT INTO `sys_role_menu` VALUES (2, 129); +INSERT INTO `sys_role_menu` VALUES (2, 130); +INSERT INTO `sys_role_menu` VALUES (2, 131); +INSERT INTO `sys_role_menu` VALUES (2, 132); +INSERT INTO `sys_role_menu` VALUES (2, 133); +INSERT INTO `sys_role_menu` VALUES (2, 134); -- ---------------------------- -- Table structure for sys_user -- ---------------------------- @@ -466,5 +499,33 @@ CREATE TABLE `gen_field_config` ( KEY `config_id` (`config_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='代码生成字段配置表'; +CREATE TABLE `sys_notice` ( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `title` VARCHAR(50) DEFAULT NULL COMMENT '通知标题', + `content` TEXT COMMENT '通知内容', + `notice_type` INT NOT NULL COMMENT '通知类型', + `release_by` BIGINT DEFAULT NULL COMMENT '发布人', + `priority` TINYINT NOT NULL COMMENT '优先级(0-低 1-中 2-高)', + `tar_type` TINYINT NOT NULL COMMENT '目标类型(0-全体 1-指定)', + `tar_ids` TEXT COMMENT '目标人id', + `release_status` TINYINT NOT NULL COMMENT '发布状态(0-未发布 1已发布 2已撤回)', + `release_time` DATETIME DEFAULT NULL COMMENT '发布时间', + `recall_time` DATETIME DEFAULT NULL COMMENT '撤回时间', + `create_by` BIGINT NOT NULL COMMENT '创建人ID', + `create_time` DATETIME NOT NULL COMMENT '创建时间', + `update_by` BIGINT DEFAULT NULL COMMENT '更新人ID', + `update_time` DATETIME DEFAULT NULL COMMENT '更新时间', + `is_deleted` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除标识(0-未删除 1-已删除)', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=INNODB COMMENT='通知公告'; + +CREATE TABLE `sys_notice_status` ( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'id', + `notice_id` BIGINT NOT NULL COMMENT '公共通知id', + `user_id` INT NOT NULL COMMENT '用户id', + `read_status` BIGINT DEFAULT NULL COMMENT '读取状态,0未读,1已读取', + `read_time` DATETIME DEFAULT NULL COMMENT '用户阅读时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE=INNODB COMMENT='用户公告状态表'; SET FOREIGN_KEY_CHECKS = 1; diff --git a/src/main/java/com/youlai/boot/common/enums/NoticeTypeEnum.java b/src/main/java/com/youlai/boot/common/enums/NoticeTypeEnum.java new file mode 100644 index 00000000..2bf5b176 --- /dev/null +++ b/src/main/java/com/youlai/boot/common/enums/NoticeTypeEnum.java @@ -0,0 +1,33 @@ +package com.youlai.boot.common.enums; + +import com.youlai.boot.common.base.IBaseEnum; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 通知类型枚举 + * 0-系统消息 + * + * @since 2024-9-1 17:33:06 + * @author Theo + */ +@Getter +@RequiredArgsConstructor +public enum NoticeTypeEnum implements IBaseEnum { + + /** + * 通知类型 + */ + SYSTEM_MESSAGE(0, "系统消息"); + + @Getter + private Integer value; + + @Getter + private String label; + + NoticeTypeEnum(Integer value, String label) { + this.value = value; + this.label = label; + } +} diff --git a/src/main/java/com/youlai/boot/common/enums/NoticeWayEnum.java b/src/main/java/com/youlai/boot/common/enums/NoticeWayEnum.java new file mode 100644 index 00000000..bf11e68c --- /dev/null +++ b/src/main/java/com/youlai/boot/common/enums/NoticeWayEnum.java @@ -0,0 +1,30 @@ +package com.youlai.boot.common.enums; + +import com.youlai.boot.common.base.IBaseEnum; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * 通知方式枚举 + * @author Theo + * @since 2024-9-2 14:32:58 + */ +@Getter +@RequiredArgsConstructor +public enum NoticeWayEnum implements IBaseEnum { + /** + * 通知方式 + */ + WEBSOCKET("webSocket", "发送websocket消息"); + + @Getter + private String value; + + @Getter + private String label; + + NoticeWayEnum(String value, String label) { + this.value = value; + this.label = label; + } +} diff --git a/src/main/java/com/youlai/boot/common/result/PageResult.java b/src/main/java/com/youlai/boot/common/result/PageResult.java index 5e0f2779..693fcb64 100644 --- a/src/main/java/com/youlai/boot/common/result/PageResult.java +++ b/src/main/java/com/youlai/boot/common/result/PageResult.java @@ -25,7 +25,7 @@ public class PageResult implements Serializable { PageResult result = new PageResult<>(); result.setCode(ResultCode.SUCCESS.getCode()); - Data data = new Data(); + Data data = new Data<>(); data.setList(page.getRecords()); data.setTotal(page.getTotal()); diff --git a/src/main/java/com/youlai/boot/common/util/CommonUtil.java b/src/main/java/com/youlai/boot/common/util/CommonUtil.java new file mode 100644 index 00000000..f039ed48 --- /dev/null +++ b/src/main/java/com/youlai/boot/common/util/CommonUtil.java @@ -0,0 +1,67 @@ +package com.youlai.boot.common.util; + +import com.youlai.boot.common.constant.SymbolConstant; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 通用工具类 + * + * @author Theo + * @since 2024-9-1 23:42:33 + * @version 1.0.0 + */ +public class CommonUtil { + + private CommonUtil(){} + + + + /** + * 将List转换为字符串 + * + * @param list List + * @param separator 分隔符 + * @return 字符串 + */ + public static String listToStr(List list, String separator) { + return list.stream().collect(Collectors.joining(separator)); + } + + /** + * 将字符串转换为List + * + * @param list List + * @return List + */ + public static String listToStr(List list) { + return listToStr(list, SymbolConstant.COMMA); + } + + /** + * 将字符串转换为List + * + * @param str 字符串 + * @return List + */ + public static List strToList(String str) { + return strToList(str, SymbolConstant.COMMA); + } + + /** + * 将字符串转换为List + * + * @param str 字符串 + * @param separator 分隔符 + * @return List + */ + public static List strToList(String str, String separator) { + return List.of(str.split(separator)); + } + + + public static String delHtmlTags(String htmlStr) { + return htmlStr.replaceAll("<[^>]+>", ""); + } +} diff --git a/src/main/java/com/youlai/boot/core/security/model/SysUserDetails.java b/src/main/java/com/youlai/boot/core/security/model/SysUserDetails.java index e7801e8e..5fbf1f6c 100644 --- a/src/main/java/com/youlai/boot/core/security/model/SysUserDetails.java +++ b/src/main/java/com/youlai/boot/core/security/model/SysUserDetails.java @@ -51,7 +51,7 @@ public class SysUserDetails implements UserDetails { .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) // 标识角色 .collect(Collectors.toSet()); } else { - authorities = Collections.EMPTY_SET; + authorities = Collections.emptySet(); } this.authorities = authorities; this.username = user.getUsername(); @@ -78,21 +78,6 @@ public class SysUserDetails implements UserDetails { return this.username; } - @Override - public boolean isAccountNonExpired() { - return true; - } - - @Override - public boolean isAccountNonLocked() { - return true; - } - - @Override - public boolean isCredentialsNonExpired() { - return true; - } - @Override public boolean isEnabled() { return this.enabled; diff --git a/src/main/java/com/youlai/boot/module/websocket/controller/WebsocketController.java b/src/main/java/com/youlai/boot/module/websocket/controller/WebsocketController.java index 4470332c..5bf9b089 100644 --- a/src/main/java/com/youlai/boot/module/websocket/controller/WebsocketController.java +++ b/src/main/java/com/youlai/boot/module/websocket/controller/WebsocketController.java @@ -1,5 +1,6 @@ package com.youlai.boot.module.websocket.controller; +import com.youlai.boot.common.enums.NoticeTypeEnum; import com.youlai.boot.system.model.dto.ChatMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -27,6 +28,7 @@ public class WebsocketController { private final SimpMessagingTemplate messagingTemplate; + /** * 广播发送消息 * @@ -56,7 +58,7 @@ public class WebsocketController { log.info("发送人:{}; 接收人:{}", sender, receiver); // 发送消息给指定用户,拼接后路径 /user/{receiver}/queue/greeting - messagingTemplate.convertAndSendToUser(receiver, "/queue/greeting", new ChatMessage(sender, message)); + messagingTemplate.convertAndSendToUser(receiver, "/queue/greeting", new ChatMessage(sender, message, NoticeTypeEnum.SYSTEM_MESSAGE)); } } diff --git a/src/main/java/com/youlai/boot/module/websocket/service/MessageService.java b/src/main/java/com/youlai/boot/module/websocket/service/MessageService.java new file mode 100644 index 00000000..040990b7 --- /dev/null +++ b/src/main/java/com/youlai/boot/module/websocket/service/MessageService.java @@ -0,0 +1,29 @@ +package com.youlai.boot.module.websocket.service; + +import com.youlai.boot.common.enums.NoticeWayEnum; +import com.youlai.boot.system.model.dto.MessageDTO; + +/** + * 消息服务接口 + * + * @author Theo + * @since 2024-9-2 14:32:58 + */ +public interface MessageService { + + + /** + * 检查消息类型 + * + * @param messageType 消息类型 + * @return 是否支持 + */ + boolean check(NoticeWayEnum messageType); + + /** + * 发送消息 + * + * @param message 消息 + */ + void sendMessage(MessageDTO message); +} diff --git a/src/main/java/com/youlai/boot/module/websocket/service/OnlineUserService.java b/src/main/java/com/youlai/boot/module/websocket/service/OnlineUserService.java deleted file mode 100644 index 3d05c20d..00000000 --- a/src/main/java/com/youlai/boot/module/websocket/service/OnlineUserService.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.youlai.boot.module.websocket.service; - -import com.youlai.boot.system.event.UserConnectionEvent; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.event.EventListener; -import org.springframework.messaging.simp.SimpMessagingTemplate; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; - -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -/** - * 在线用户服务 - * - * @author Ray - * @since 2.3.0 - */ -@Service -@RequiredArgsConstructor -@Slf4j -public class OnlineUserService { - - private final SimpMessagingTemplate messagingTemplate; - - private final Set onlineUsers = ConcurrentHashMap.newKeySet(); - - @EventListener - public void handleUserConnectionEvent(UserConnectionEvent event) { - String username = event.getUsername(); - if (event.isConnected()) { - onlineUsers.add(username); - log.info("User connected: {}", username); - } else { - onlineUsers.remove(username); - log.info("User disconnected: {}", username); - } - // 推送在线用户人数 - messagingTemplate.convertAndSend("/topic/onlineUserCount", onlineUsers.size()); - } - - @Scheduled(fixedRate = 5000) - public void sendOnlineUserCount() { - messagingTemplate.convertAndSend("/topic/onlineUserCount", onlineUsers.size()); - } -} diff --git a/src/main/java/com/youlai/boot/module/websocket/service/impl/WebsocketServiceImpl.java b/src/main/java/com/youlai/boot/module/websocket/service/impl/WebsocketServiceImpl.java new file mode 100644 index 00000000..aab4fd50 --- /dev/null +++ b/src/main/java/com/youlai/boot/module/websocket/service/impl/WebsocketServiceImpl.java @@ -0,0 +1,96 @@ +package com.youlai.boot.module.websocket.service.impl; + +import com.youlai.boot.common.enums.NoticeWayEnum; +import com.youlai.boot.common.enums.NoticeTypeEnum; +import com.youlai.boot.module.websocket.service.MessageService; +import com.youlai.boot.system.event.UserConnectionEvent; +import com.youlai.boot.system.model.dto.ChatMessage; +import com.youlai.boot.system.model.dto.MessageDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * WebSocket消息服务实现类 + * + * @author ray + * @since 2024-9-2 14:32:58 + */ +@Service +@Slf4j +@RequiredArgsConstructor +public class WebsocketServiceImpl implements MessageService { + + private final SimpMessagingTemplate messagingTemplate; + + private final Set onlineUsers = ConcurrentHashMap.newKeySet(); + + /** + * 用户连接事件处理 + * + * @param event 用户连接事件 + */ + @EventListener + public void handleUserConnectionEvent(UserConnectionEvent event) { + String username = event.getUsername(); + if (event.isConnected()) { + onlineUsers.add(username); + log.info("User connected: {}", username); + } else { + onlineUsers.remove(username); + log.info("User disconnected: {}", username); + } + // 推送在线用户人数 + messagingTemplate.convertAndSend("/topic/onlineUserCount", onlineUsers.size()); + } + + /** + * 定时推送在线用户人数 + */ + @Scheduled(fixedRate = 5000) + public void sendOnlineUserCount() { + messagingTemplate.convertAndSend("/topic/onlineUserCount", onlineUsers.size()); + } + + + /** + * 策略模式检查 + * + * @param noticeWayEnum 通知方式 + * @return boolean 是否支持 + */ + @Override + public boolean check(NoticeWayEnum noticeWayEnum) { + return noticeWayEnum.equals(NoticeWayEnum.WEBSOCKET); + } + + /** + * 发送消息 + * + * @param message 消息 + */ + @Override + public void sendMessage(MessageDTO message) { + List users = null; + if(message.getReceiver() == null || message.getReceiver().isEmpty()){ + // 发送给所有在线用户 离线用户不发送,因为离线用户下次登录会直接查询未读消息 + users = new ArrayList<>(onlineUsers); + }else{ + users = message.getReceiver().stream().filter(onlineUsers::contains).collect(Collectors.toList()); + } + //获取当前用户 + ChatMessage chatMessage = new ChatMessage(message.getSender(), message.getContent(), NoticeTypeEnum.SYSTEM_MESSAGE); + users.forEach(receiver -> { + messagingTemplate.convertAndSendToUser(receiver, "/queue/message", chatMessage); + }); + } +} diff --git a/src/main/java/com/youlai/boot/system/controller/DictController.java b/src/main/java/com/youlai/boot/system/controller/DictController.java index 1d79d993..71d2e6c0 100644 --- a/src/main/java/com/youlai/boot/system/controller/DictController.java +++ b/src/main/java/com/youlai/boot/system/controller/DictController.java @@ -14,6 +14,7 @@ import com.youlai.boot.system.service.DictService; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @@ -73,7 +74,7 @@ public class DictController { @PostMapping @PreAuthorize("@ss.hasPerm('sys:dict:add')") @RepeatSubmit - public Result saveDict(@RequestBody DictForm formData) { + public Result saveDict(@Valid @RequestBody DictForm formData) { boolean result = dictService.saveDict(formData); return Result.judge(result); } diff --git a/src/main/java/com/youlai/boot/system/controller/NoticeController.java b/src/main/java/com/youlai/boot/system/controller/NoticeController.java new file mode 100644 index 00000000..3e2ac049 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/controller/NoticeController.java @@ -0,0 +1,131 @@ +package com.youlai.boot.system.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.youlai.boot.common.result.PageResult; +import com.youlai.boot.common.result.Result; +import com.youlai.boot.system.model.form.NoticeForm; +import com.youlai.boot.system.model.query.NoticeQuery; +import com.youlai.boot.system.model.vo.NoticeStatusVO; +import com.youlai.boot.system.model.vo.NoticeVO; +import com.youlai.boot.system.service.NoticeService; +import com.youlai.boot.system.service.NoticeStatusService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +/** + * 通知公告前端控制层 + * + * @author youlaitech + * @since 2024-08-27 10:31 + */ +@Tag(name = "通知公告接口") +@RestController +@RequestMapping("/api/v1/notices") +@RequiredArgsConstructor +public class NoticeController { + + private final NoticeService noticeService; + + private final NoticeStatusService noticeStatusService; + + @Operation(summary = "通知公告分页列表") + @GetMapping("/page") + @PreAuthorize("@ss.hasPerm('system:notice:query')") + public PageResult getNoticePage(NoticeQuery queryParams ) { + IPage result = noticeService.getNoticePage(queryParams); + return PageResult.success(result); + } + + @Operation(summary = "新增通知公告") + @PostMapping + @PreAuthorize("@ss.hasPerm('system:notice:add')") + public Result saveNotice(@RequestBody @Valid NoticeForm formData ) { + boolean result = noticeService.saveNotice(formData); + return Result.judge(result); + } + + @Operation(summary = "获取通知公告表单数据") + @GetMapping("/{id}/form") + @PreAuthorize("@ss.hasPerm('system:notice:edit')") + public Result getNoticeForm( + @Parameter(description = "通知公告ID") @PathVariable Long id + ) { + NoticeForm formData = noticeService.getNoticeFormData(id); + return Result.success(formData); + } + @Operation(summary = "管理页面查看通知公告") + @GetMapping("/detail/{id}") + public Result getReadNoticeDetail( + @Parameter(description = "通知公告ID")@PathVariable Long id) { + return Result.success(noticeService.getReadNoticeDetail(id)); + } + + @Operation(summary = "修改通知公告") + @PutMapping(value = "/{id}") + @PreAuthorize("@ss.hasPerm('system:notice:edit')") + public Result updateNotice( + @Parameter(description = "通知公告ID") @PathVariable Long id, + @RequestBody @Validated NoticeForm formData + ) { + boolean result = noticeService.updateNotice(id, formData); + return Result.judge(result); + } + + @Operation(summary = "发布通知公告") + @PatchMapping(value = "/release/{id}") + @PreAuthorize("@ss.hasPerm('system:notice:release')") + public Result releaseNotice(@Parameter(description = "通知公告ID") @PathVariable Long id) { + boolean result = noticeService.releaseNotice(id); + return Result.judge(result); + } + + @Operation(summary = "撤回通知公告") + @PatchMapping(value = "/recall/{id}") + @PreAuthorize("@ss.hasPerm('system:notice:recall')") + public Result recallNotice(@Parameter(description = "通知公告ID") @PathVariable Long id) { + boolean result = noticeService.recallNotice(id); + return Result.judge(result); + } + + @Operation(summary = "删除通知公告") + @DeleteMapping("/{ids}") + @PreAuthorize("@ss.hasPerm('system:notice:delete')") + public Result deleteNotices( + @Parameter(description = "通知公告ID,多个以英文逗号(,)分割") @PathVariable String ids + ) { + boolean result = noticeService.deleteNotices(ids); + return Result.judge(result); + } + + @Operation(summary = "获取未读的通知公告") + @GetMapping("/unread") + public Result listUnreadNotices() { + return Result.success(noticeStatusService.listUnreadNotices()); + } + + @Operation(summary = "阅读通知公告") + @PatchMapping("/read/{id}") + public Result readNotice(@PathVariable Long id) { + return Result.success(noticeService.readNotice(id)); + } + + @Operation(summary = "全部已读") + @PatchMapping("/readAll") + public Result readAll() { + noticeStatusService.readAll(); + return Result.success(); + } + + @Operation(summary = "获取我的通知公告") + @GetMapping("/my/page") + public PageResult getMyNoticePage(NoticeQuery queryParams) { + IPage result = noticeService.getMyNoticePage(queryParams); + return PageResult.success(result); + } +} diff --git a/src/main/java/com/youlai/boot/system/controller/UserController.java b/src/main/java/com/youlai/boot/system/controller/UserController.java index a17dd63d..bac8aa85 100644 --- a/src/main/java/com/youlai/boot/system/controller/UserController.java +++ b/src/main/java/com/youlai/boot/system/controller/UserController.java @@ -4,35 +4,35 @@ import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; -import com.youlai.boot.system.model.entity.User; -import com.youlai.boot.system.model.form.*; +import com.youlai.boot.common.annotation.Log; +import com.youlai.boot.common.annotation.RepeatSubmit; +import com.youlai.boot.common.enums.ContactType; +import com.youlai.boot.common.enums.LogModuleEnum; +import com.youlai.boot.common.model.Option; import com.youlai.boot.common.result.PageResult; import com.youlai.boot.common.result.Result; -import com.youlai.boot.common.enums.ContactType; -import com.youlai.boot.system.model.vo.UserProfileVO; -import com.youlai.boot.core.security.util.SecurityUtils; import com.youlai.boot.common.util.ExcelUtils; -import com.youlai.boot.common.enums.LogModuleEnum; -import com.youlai.boot.system.model.dto.UserImportDTO; -import com.youlai.boot.common.annotation.RepeatSubmit; +import com.youlai.boot.core.security.util.SecurityUtils; import com.youlai.boot.system.listener.UserImportListener; -import com.youlai.boot.system.model.query.UserPageQuery; import com.youlai.boot.system.model.dto.UserExportDTO; +import com.youlai.boot.system.model.dto.UserImportDTO; +import com.youlai.boot.system.model.entity.User; +import com.youlai.boot.system.model.form.*; +import com.youlai.boot.system.model.query.UserPageQuery; import com.youlai.boot.system.model.vo.UserInfoVO; import com.youlai.boot.system.model.vo.UserPageVO; -import com.youlai.boot.common.annotation.Log; +import com.youlai.boot.system.model.vo.UserProfileVO; import com.youlai.boot.system.service.UserService; +import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; - -import jakarta.servlet.ServletOutputStream; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.Valid; import org.springframework.web.multipart.MultipartFile; import java.io.File; @@ -228,6 +228,10 @@ public class UserController { } - - + @Operation(summary = "用户下拉选项") + @GetMapping("/options") + public Result>> listUserOptions() { + List> list = userService.listUserOptions(); + return Result.success(list); + } } diff --git a/src/main/java/com/youlai/boot/system/converter/NoticeConverter.java b/src/main/java/com/youlai/boot/system/converter/NoticeConverter.java new file mode 100644 index 00000000..3ba79b45 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/converter/NoticeConverter.java @@ -0,0 +1,39 @@ +package com.youlai.boot.system.converter; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.youlai.boot.system.model.bo.NoticeBO; +import com.youlai.boot.system.model.entity.Notice; +import com.youlai.boot.system.model.form.NoticeForm; +import com.youlai.boot.system.model.vo.NoticeVO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; + +/** + * 通知公告对象转换器 + * + * @author youlaitech + * @since 2024-08-27 10:31 + */ +@Mapper(componentModel = "spring") +public interface NoticeConverter{ + + @Mappings({ + @Mapping(target = "tarIds", expression = "java(com.youlai.boot.common.util.CommonUtil.strToList(entity.getTarIds()))") + }) + NoticeForm toForm(Notice entity); + + @Mappings({ + @Mapping(target = "tarIds", expression = "java(com.youlai.boot.common.util.CommonUtil.listToStr(formData.getTarIds()))") + }) + Notice toEntity(NoticeForm formData); + + NoticeVO toVO(Notice notice); + + Page toPageVo(Page noticePage); + + @Mappings({ + }) + NoticeVO toPageVo(NoticeBO bo); + +} diff --git a/src/main/java/com/youlai/boot/system/handler/MessageHandler.java b/src/main/java/com/youlai/boot/system/handler/MessageHandler.java new file mode 100644 index 00000000..689a3816 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/handler/MessageHandler.java @@ -0,0 +1,35 @@ +package com.youlai.boot.system.handler; + +import com.youlai.boot.module.websocket.service.MessageService; +import com.youlai.boot.system.model.dto.MessageDTO; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * 消息处理器 + * + * @author Theo + * @since 2024-9-2 14:32:58 + */ +@Component +@RequiredArgsConstructor +public class MessageHandler { + + private final List messageServices; + + + /** + * 发送消息 + * 如果后面有多种消息发送方式,可以设置MessageDTO中的noticeWay,调用不同的消息发送方式,实现消息多种发送方式 + * @param messageDTO 消息载体 + */ + public void sendMessage(MessageDTO messageDTO) { + messageServices.forEach(messageService -> { + if (messageService.check(messageDTO.getNoticeWay())) { + messageService.sendMessage(messageDTO); + } + }); + } +} diff --git a/src/main/java/com/youlai/boot/system/mapper/NoticeMapper.java b/src/main/java/com/youlai/boot/system/mapper/NoticeMapper.java new file mode 100644 index 00000000..5a11f119 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/mapper/NoticeMapper.java @@ -0,0 +1,37 @@ +package com.youlai.boot.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.youlai.boot.system.model.bo.NoticeBO; +import com.youlai.boot.system.model.entity.Notice; +import com.youlai.boot.system.model.query.NoticeQuery; +import com.youlai.boot.system.model.vo.NoticeVO; +import com.youlai.boot.system.model.vo.NoticeDetailVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 通知公告Mapper接口 + * + * @author youlaitech + * @since 2024-08-27 10:31 + */ +@Mapper +public interface NoticeMapper extends BaseMapper { + + /** + * 获取通知公告分页数据 + * + * @param page 分页对象 + * @param queryParams 查询参数 + * @return 通知公告分页数据 + */ + Page getNoticePage(Page page, @Param("queryParams") NoticeQuery queryParams); + + /** + * 获取阅读时通知公告详情 + * @param id 通知公告ID + * @return 通知公告详情 + */ + NoticeDetailVO getReadNoticeVO(@Param("id") Long id); +} diff --git a/src/main/java/com/youlai/boot/system/mapper/NoticeStatusMapper.java b/src/main/java/com/youlai/boot/system/mapper/NoticeStatusMapper.java new file mode 100644 index 00000000..7de27f85 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/mapper/NoticeStatusMapper.java @@ -0,0 +1,38 @@ +package com.youlai.boot.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.youlai.boot.system.model.entity.NoticeStatus; +import com.youlai.boot.system.model.query.NoticeQuery; +import com.youlai.boot.system.model.vo.NoticeStatusVO; +import com.youlai.boot.system.model.vo.NoticeVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户公告状态Mapper接口 + * + * @author youlaitech + * @since 2024-08-28 16:56 + */ +@Mapper +public interface NoticeStatusMapper extends BaseMapper { + + /** + * 获取未读的通知公告 + * @param userId 用户ID + * @return 公告列表 + */ + List listUnreadNotices(@Param("userId")Long userId); + + /** + * 分页获取我的通知公告 + * @param page 分页对象 + * @param queryParams 查询参数 + * @return 通知公告分页列表 + */ + IPage getMyNoticePage(Page page, @Param("queryParams") NoticeQuery queryParams); +} diff --git a/src/main/java/com/youlai/boot/system/model/bo/NoticeBO.java b/src/main/java/com/youlai/boot/system/model/bo/NoticeBO.java new file mode 100644 index 00000000..e6f9fbc8 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/bo/NoticeBO.java @@ -0,0 +1,46 @@ +package com.youlai.boot.system.model.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.time.LocalDateTime; + +/** + * 通知公告业务对象 + * + * @author Theo + * @since 2024-09-01 10:31 + * @version 1.0.0 + */ +@Data +public class NoticeBO { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "通知标题") + private String title; + + @Schema(description = "通知类型") + private Integer noticeType; + + @Schema(description = "发布人") + private String releaseBy; + + @Schema(description = "优先级(0-低 1-中 2-高)") + private Integer priority; + + @Schema(description = "目标类型(0-全体 1-指定)") + private Integer tarType; + + @Schema(description = "发布状态(0-未发布 1已发布 2已撤回)") + private Integer releaseStatus; + + @Schema(description = "发布时间") + private LocalDateTime releaseTime; + + @Schema(description = "撤回时间") + private LocalDateTime recallTime; +} diff --git a/src/main/java/com/youlai/boot/system/model/dto/ChatMessage.java b/src/main/java/com/youlai/boot/system/model/dto/ChatMessage.java index 7e2a6e79..e4ce3db4 100644 --- a/src/main/java/com/youlai/boot/system/model/dto/ChatMessage.java +++ b/src/main/java/com/youlai/boot/system/model/dto/ChatMessage.java @@ -1,11 +1,12 @@ package com.youlai.boot.system.model.dto; +import com.youlai.boot.common.enums.NoticeTypeEnum; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; /** - * WebSocket 消息体 + * 系统消息体 */ @Data @AllArgsConstructor @@ -22,4 +23,9 @@ public class ChatMessage { */ private String content; + /** + * 消息类型 + */ + private NoticeTypeEnum noticeType; + } diff --git a/src/main/java/com/youlai/boot/system/model/dto/MessageDTO.java b/src/main/java/com/youlai/boot/system/model/dto/MessageDTO.java new file mode 100644 index 00000000..5b792c71 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/dto/MessageDTO.java @@ -0,0 +1,30 @@ +package com.youlai.boot.system.model.dto; + +import com.youlai.boot.common.enums.NoticeWayEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 消息载体 + * + * @author Theo + * @since 2024-9-2 14:32:58 + * @version 1.0.0 + */ +@Data +public class MessageDTO { + + @Schema(description = "消息内容") + private String content; + + @Schema(description = "发送者") + private String sender; + + @Schema(description = "接收者") + private List receiver; + + @Schema(description = "通知方式") + private NoticeWayEnum noticeWay; +} diff --git a/src/main/java/com/youlai/boot/system/model/entity/Config.java b/src/main/java/com/youlai/boot/system/model/entity/Config.java index a82a515c..1100ee96 100644 --- a/src/main/java/com/youlai/boot/system/model/entity/Config.java +++ b/src/main/java/com/youlai/boot/system/model/entity/Config.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.annotation.*; import com.youlai.boot.common.base.BaseEntity; import lombok.Data; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.EqualsAndHashCode; /** * 系统配置 实体 @@ -11,9 +12,10 @@ import io.swagger.v3.oas.annotations.media.Schema; * @author Theo * @since 2024-07-29 11:17:26 */ +@Data +@EqualsAndHashCode(callSuper = true) @Schema(description = "系统配置") @TableName("sys_config") -@Data public class Config extends BaseEntity { @Schema(description = "配置名称") diff --git a/src/main/java/com/youlai/boot/system/model/entity/Dict.java b/src/main/java/com/youlai/boot/system/model/entity/Dict.java index 586b4fa4..6604f801 100644 --- a/src/main/java/com/youlai/boot/system/model/entity/Dict.java +++ b/src/main/java/com/youlai/boot/system/model/entity/Dict.java @@ -3,6 +3,7 @@ package com.youlai.boot.system.model.entity; import com.baomidou.mybatisplus.annotation.TableName; import com.youlai.boot.common.base.BaseEntity; import lombok.Data; +import lombok.EqualsAndHashCode; /** * 字典实体 @@ -10,8 +11,9 @@ import lombok.Data; * @author haoxr * @since 2022/12/17 */ -@TableName("sys_dict") @Data +@TableName("sys_dict") +@EqualsAndHashCode(callSuper = true) public class Dict extends BaseEntity { /** diff --git a/src/main/java/com/youlai/boot/system/model/entity/Notice.java b/src/main/java/com/youlai/boot/system/model/entity/Notice.java new file mode 100644 index 00000000..d8f6a2d9 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/entity/Notice.java @@ -0,0 +1,79 @@ +package com.youlai.boot.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.youlai.boot.common.base.BaseEntity; +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; +/** + * 通知公告实体对象 + * + * @author youlaitech + * @since 2024-08-27 10:31 + */ +@Getter +@Setter +@TableName("sys_notice") +public class Notice extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** + * 通知标题 + */ + private String title; + /** + * 通知内容 + */ + private String content; + /** + * 通知类型 + */ + private Integer noticeType; + /** + * 发布人 + */ + private Long releaseBy; + /** + * 优先级(0-低 1-中 2-高) + */ + private Integer priority; + /** + * 目标类型(0-全体 1-指定) + */ + private Integer tarType; + /** + * 目标ID + */ + private String tarIds; + /** + * 发布状态(0-未发布 1已发布 2已撤回) + */ + private Integer releaseStatus; + /** + * 发布时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime releaseTime; + /** + * 撤回时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime recallTime; + /** + * 创建人ID + */ + private Long createBy; + /** + * 更新人ID + */ + private Long updateBy; + /** + * 逻辑删除标识(0-未删除 1-已删除) + */ + @TableLogic(value = "0", delval = "1") + private Integer isDeleted; +} diff --git a/src/main/java/com/youlai/boot/system/model/entity/NoticeStatus.java b/src/main/java/com/youlai/boot/system/model/entity/NoticeStatus.java new file mode 100644 index 00000000..4cdc34c9 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/entity/NoticeStatus.java @@ -0,0 +1,47 @@ +package com.youlai.boot.system.model.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 用户公告状态实体对象 + * + * @author youlaitech + * @since 2024-08-28 16:56 + */ +@Getter +@Setter +@TableName("sys_notice_status") +public class NoticeStatus implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键ID + */ + @TableId(type = IdType.AUTO) + private Long id; + + /** + * 公共通知id + */ + private Long noticeId; + /** + * 用户id + */ + private Long userId; + /** + * 读取状态,0未读,1已读 + */ + private Integer readStatus; + /** + * 用户阅读时间 + */ + private LocalDateTime readTime; +} diff --git a/src/main/java/com/youlai/boot/system/model/form/NoticeForm.java b/src/main/java/com/youlai/boot/system/model/form/NoticeForm.java new file mode 100644 index 00000000..980517bd --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/form/NoticeForm.java @@ -0,0 +1,55 @@ +package com.youlai.boot.system.model.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.validator.constraints.Range; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 通知公告表单对象 + * + * @author youlaitech + * @since 2024-08-27 10:31 + */ +@Getter +@Setter +@Schema(description = "通知公告表单对象") +public class NoticeForm implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "通知ID") + private Long id; + + @Schema(description = "通知标题") + @NotBlank(message = "通知标题不能为空") + @Size(max=50, message="通知标题长度不能超过50个字符") + private String title; + + @Schema(description = "通知内容") + @NotBlank(message = "通知内容不能为空") + @Size(max=65535, message="通知内容长度不能超过65535个字符") + private String content; + + @Schema(description = "通知类型") + private Integer noticeType; + + @Schema(description = "优先级(0-低 1-中 2-高)") + @Range(min = 0, max = 2, message = "优先级取值范围[0,2]") + private Integer priority; + + @Schema(description = "目标类型(0-全体 1-指定)") + @Range(min = 0, max = 1, message = "目标类型取值范围[0,1]") + private Integer tarType; + + @Schema(description = "接收人ID集合") + private List tarIds; + +} diff --git a/src/main/java/com/youlai/boot/system/model/form/NoticeStatusForm.java b/src/main/java/com/youlai/boot/system/model/form/NoticeStatusForm.java new file mode 100644 index 00000000..d53689aa --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/form/NoticeStatusForm.java @@ -0,0 +1,26 @@ +package com.youlai.system.model.form; + +import java.io.Serial; +import java.io.Serializable; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import java.time.LocalDateTime; +import jakarta.validation.constraints.*; + +/** + * 用户公告状态表单对象 + * + * @author youlaitech + * @since 2024-08-28 16:56 + */ +@Getter +@Setter +@Schema(description = "用户公告状态表单对象") +public class NoticeStatusForm implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + +} diff --git a/src/main/java/com/youlai/boot/system/model/query/DictPageQuery.java b/src/main/java/com/youlai/boot/system/model/query/DictPageQuery.java index 282e7947..3e4696b4 100644 --- a/src/main/java/com/youlai/boot/system/model/query/DictPageQuery.java +++ b/src/main/java/com/youlai/boot/system/model/query/DictPageQuery.java @@ -4,9 +4,11 @@ package com.youlai.boot.system.model.query; import com.youlai.boot.common.base.BasePageQuery; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import lombok.EqualsAndHashCode; -@Schema(description ="字典数据项分页查询对象") @Data +@EqualsAndHashCode(callSuper = true) +@Schema(description ="字典数据项分页查询对象") public class DictPageQuery extends BasePageQuery { @Schema(description="关键字(字典项名称)") diff --git a/src/main/java/com/youlai/boot/system/model/query/NoticeQuery.java b/src/main/java/com/youlai/boot/system/model/query/NoticeQuery.java new file mode 100644 index 00000000..f58a9f6e --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/query/NoticeQuery.java @@ -0,0 +1,34 @@ +package com.youlai.boot.system.model.query; + +import com.youlai.boot.common.base.BasePageQuery; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * 通知公告分页查询对象 + * + * @author youlaitech + * @since 2024-08-27 10:31 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description ="通知公告查询对象") +public class NoticeQuery extends BasePageQuery { + + private static final long serialVersionUID = 1L; + + @Schema(description = "通知标题") + private String title; + + @Schema(description = "发布状态(0-未发布 1已发布 2已撤回)") + private Integer releaseStatus; + + @Schema(description = "发布时间") + private List releaseTime; + + @Schema(description = "查询人ID") + private Long userId; +} diff --git a/src/main/java/com/youlai/boot/system/model/query/NoticeStatusQuery.java b/src/main/java/com/youlai/boot/system/model/query/NoticeStatusQuery.java new file mode 100644 index 00000000..cfc1d5be --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/query/NoticeStatusQuery.java @@ -0,0 +1,36 @@ +package com.youlai.boot.system.model.query; + +import com.youlai.boot.common.base.BasePageQuery; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import java.util.List; + +/** + * 用户公告状态分页查询对象 + * + * @author youlaitech + * @since 2024-08-28 16:56 + */ +@Schema(description ="用户公告状态查询对象") +@Getter +@Setter +public class NoticeStatusQuery extends BasePageQuery { + + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "公共通知id") + private Long noticeId; + + @Schema(description = "用户id") + private Integer userId; + + @Schema(description = "读取状态,0未读,1已读取") + private Long readStatus; + + @Schema(description = "用户阅读时间") + private List readTime; +} diff --git a/src/main/java/com/youlai/boot/system/model/query/PermPageQuery.java b/src/main/java/com/youlai/boot/system/model/query/PermPageQuery.java index 95cb5485..07e02011 100644 --- a/src/main/java/com/youlai/boot/system/model/query/PermPageQuery.java +++ b/src/main/java/com/youlai/boot/system/model/query/PermPageQuery.java @@ -3,6 +3,7 @@ package com.youlai.boot.system.model.query; import com.youlai.boot.common.base.BasePageQuery; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import lombok.EqualsAndHashCode; /** * 权限分页查询对象 @@ -11,7 +12,8 @@ import lombok.Data; * @since 2022/1/14 22:22 */ @Data -@Schema +@Schema +@EqualsAndHashCode(callSuper = true) public class PermPageQuery extends BasePageQuery { @Schema(description="权限名称") diff --git a/src/main/java/com/youlai/boot/system/model/query/UserPageQuery.java b/src/main/java/com/youlai/boot/system/model/query/UserPageQuery.java index 5d7f7157..3e2e7f74 100644 --- a/src/main/java/com/youlai/boot/system/model/query/UserPageQuery.java +++ b/src/main/java/com/youlai/boot/system/model/query/UserPageQuery.java @@ -3,6 +3,7 @@ package com.youlai.boot.system.model.query; import com.youlai.boot.common.base.BasePageQuery; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; +import lombok.EqualsAndHashCode; import java.util.List; @@ -12,8 +13,9 @@ import java.util.List; * @author haoxr * @since 2022/1/14 */ -@Schema(description ="用户分页查询对象") @Data +@EqualsAndHashCode(callSuper = true) +@Schema(description ="用户分页查询对象") public class UserPageQuery extends BasePageQuery { @Schema(description="关键字(用户名/昵称/手机号)") diff --git a/src/main/java/com/youlai/boot/system/model/vo/NoticeDetailVO.java b/src/main/java/com/youlai/boot/system/model/vo/NoticeDetailVO.java new file mode 100644 index 00000000..8070fdea --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/vo/NoticeDetailVO.java @@ -0,0 +1,42 @@ +package com.youlai.boot.system.model.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 阅读通知公告VO + * + * @author Theo + * @since 2024-9-8 01:25:06 + */ +@Data +public class NoticeDetailVO { + + @Schema(description = "通知ID") + private Long id; + + @Schema(description = "通知标题") + private String title; + + @Schema(description = "通知内容") + private String content; + + @Schema(description = "通知类型") + private String noticeType; + + @Schema(description = "发布人") + private String releaseBy; + + @Schema(description = "优先级(0-低 1-中 2-高)") + private Integer priority; + + @Schema(description = "发布状态(0-未发布 1已发布 2已撤回) 冗余字段,方便判断是否已经发布") + private Integer releaseStatus; + + @Schema(description = "发布时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime releaseTime; +} diff --git a/src/main/java/com/youlai/boot/system/model/vo/NoticeStatusVO.java b/src/main/java/com/youlai/boot/system/model/vo/NoticeStatusVO.java new file mode 100644 index 00000000..119638f1 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/vo/NoticeStatusVO.java @@ -0,0 +1,41 @@ +package com.youlai.boot.system.model.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * 用户公告状态VO + * + * @author Theo + * @since 2024-08-28 16:56 + */ +@Data +@Schema(description = "用户公告状态VO") +public class NoticeStatusVO { + + @Schema(description = "通知ID") + private Long id; + + @Schema(description = "通知标题") + private String title; + + @Schema(description = "通知类型") + private String noticeType; + + @Schema(description = "发布人") + private String releaseBy; + + @Schema(description = "优先级(0-低 1-中 2-高)") + private Integer priority; + + @Schema(description = "发布时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime releaseTime; + + @Schema(description = "是否已读") + private Integer readStatus; + +} diff --git a/src/main/java/com/youlai/boot/system/model/vo/NoticeVO.java b/src/main/java/com/youlai/boot/system/model/vo/NoticeVO.java new file mode 100644 index 00000000..70c3789f --- /dev/null +++ b/src/main/java/com/youlai/boot/system/model/vo/NoticeVO.java @@ -0,0 +1,53 @@ +package com.youlai.boot.system.model.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 通知公告视图对象 + * + * @author youlaitech + * @since 2024-08-27 10:31 + */ +@Getter +@Setter +@Schema( description = "通知公告视图对象") +public class NoticeVO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + + @Schema(description = "通知标题") + private String title; + + @Schema(description = "通知类型") + private String noticeType; + + @Schema(description = "发布人") + private String releaseBy; + + @Schema(description = "优先级(0-低 1-中 2-高)") + private Integer priority; + + @Schema(description = "目标类型(0-全体 1-指定)") + private Integer tarType; + + @Schema(description = "发布状态(0-未发布 1已发布 2已撤回)") + private Integer releaseStatus; + + @Schema(description = "发布时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime releaseTime; + + @Schema(description = "撤回时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime recallTime; +} diff --git a/src/main/java/com/youlai/boot/system/service/NoticeService.java b/src/main/java/com/youlai/boot/system/service/NoticeService.java new file mode 100644 index 00000000..8b6ddebf --- /dev/null +++ b/src/main/java/com/youlai/boot/system/service/NoticeService.java @@ -0,0 +1,97 @@ +package com.youlai.boot.system.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.youlai.boot.system.model.entity.Notice; +import com.youlai.boot.system.model.form.NoticeForm; +import com.youlai.boot.system.model.query.NoticeQuery; +import com.youlai.boot.system.model.vo.NoticeStatusVO; +import com.youlai.boot.system.model.vo.NoticeVO; +import com.youlai.boot.system.model.vo.NoticeDetailVO; + +/** + * 通知公告服务类 + * + * @author youlaitech + * @since 2024-08-27 10:31 + */ +public interface NoticeService extends IService { + + /** + *通知公告分页列表 + * + * @return 通知公告分页列表 + */ + IPage getNoticePage(NoticeQuery queryParams); + + /** + * 获取通知公告表单数据 + * + * @param id 通知公告ID + * @return 通知公告表单对象 + */ + NoticeForm getNoticeFormData(Long id); + + /** + * 新增通知公告 + * + * @param formData 通知公告表单对象 + * @return 是否新增成功 + */ + boolean saveNotice(NoticeForm formData); + + /** + * 修改通知公告 + * + * @param id 通知公告ID + * @param formData 通知公告表单对象 + * @return 是否修改成功 + */ + boolean updateNotice(Long id, NoticeForm formData); + + /** + * 删除通知公告 + * + * @param ids 通知公告ID,多个以英文逗号(,)分割 + * @return 是否删除成功 + */ + boolean deleteNotices(String ids); + + /** + * 发布通知公告 + * + * @param id 通知公告ID + * @return 是否发布成功 + */ + boolean releaseNotice(Long id); + + /** + * 撤回通知公告 + * + * @param id 通知公告ID + * @return 是否撤回成功 + */ + boolean recallNotice(Long id); + + /** + * 阅读通知公告 + * + * @param id 通知公告ID + * @return 通知公告对象 + */ + NoticeDetailVO readNotice(Long id); + + /** + * 获取阅读时通知公告详情 + * @param id 通知公告ID + * @return 通知公告详情 + */ + NoticeDetailVO getReadNoticeDetail(Long id); + + /** + * 获取我的通知公告分页列表 + * @param queryParams 查询参数 + * @return 通知公告分页列表 + */ + IPage getMyNoticePage(NoticeQuery queryParams); +} diff --git a/src/main/java/com/youlai/boot/system/service/NoticeStatusService.java b/src/main/java/com/youlai/boot/system/service/NoticeStatusService.java new file mode 100644 index 00000000..aae06907 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/service/NoticeStatusService.java @@ -0,0 +1,40 @@ +package com.youlai.boot.system.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import com.youlai.boot.system.model.entity.NoticeStatus; +import com.youlai.boot.system.model.query.NoticeQuery; +import com.youlai.boot.system.model.vo.NoticeStatusVO; +import com.youlai.boot.system.model.vo.NoticeVO; + +import java.util.List; + +/** + * 用户公告状态服务类 + * + * @author youlaitech + * @since 2024-08-28 16:56 + */ +public interface NoticeStatusService extends IService { + + /** + * 获取未读的通知公告 + * @return 公告列表 + */ + List listUnreadNotices(); + + /** + * 全部标记为已读 + * @return 是否成功 + */ + boolean readAll(); + + /** + * 分页获取我的通知公告 + * @param page 分页对象 + * @param queryParams 查询参数 + * @return 我的通知公告分页列表 + */ + IPage getMyNoticePage(Page page, NoticeQuery queryParams); +} diff --git a/src/main/java/com/youlai/boot/system/service/UserService.java b/src/main/java/com/youlai/boot/system/service/UserService.java index 45eadd78..0cecd5a2 100644 --- a/src/main/java/com/youlai/boot/system/service/UserService.java +++ b/src/main/java/com/youlai/boot/system/service/UserService.java @@ -4,6 +4,7 @@ package com.youlai.boot.system.service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; import com.youlai.boot.common.enums.ContactType; +import com.youlai.boot.common.model.Option; import com.youlai.boot.system.model.dto.UserAuthInfo; import com.youlai.boot.system.model.dto.UserExportDTO; import com.youlai.boot.system.model.entity.User; @@ -147,7 +148,14 @@ public interface UserService extends IService { * 修改当前用户邮箱 * * @param data 表单数据 - * @return + * @return {@link Boolean} 是否绑定成功 */ boolean bindEmail(EmailChangeForm data); + + /** + * 获取用户选项列表 + * + * @return {@link List>} 用户选项列表 + */ + List> listUserOptions(); } diff --git a/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java new file mode 100644 index 00000000..7a1641d7 --- /dev/null +++ b/src/main/java/com/youlai/boot/system/service/impl/NoticeServiceImpl.java @@ -0,0 +1,278 @@ +package com.youlai.boot.system.service.impl; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.youlai.boot.common.constant.SymbolConstant; +import com.youlai.boot.common.enums.NoticeWayEnum; +import com.youlai.boot.core.security.util.SecurityUtils; +import com.youlai.boot.system.converter.NoticeConverter; +import com.youlai.boot.system.handler.MessageHandler; +import com.youlai.boot.system.mapper.NoticeMapper; +import com.youlai.boot.system.model.bo.NoticeBO; +import com.youlai.boot.system.model.dto.MessageDTO; +import com.youlai.boot.system.model.entity.Notice; +import com.youlai.boot.system.model.entity.NoticeStatus; +import com.youlai.boot.system.model.entity.User; +import com.youlai.boot.system.model.form.NoticeForm; +import com.youlai.boot.system.model.query.NoticeQuery; +import com.youlai.boot.system.model.vo.NoticeStatusVO; +import com.youlai.boot.system.model.vo.NoticeVO; +import com.youlai.boot.system.model.vo.NoticeDetailVO; +import com.youlai.boot.system.service.NoticeService; +import com.youlai.boot.system.service.NoticeStatusService; +import com.youlai.boot.system.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 通知公告服务实现类 + * + * @author youlaitech + * @since 2024-08-27 10:31 + */ +@Service +@RequiredArgsConstructor +public class NoticeServiceImpl extends ServiceImpl implements NoticeService { + + private final NoticeConverter noticeConverter; + + private final MessageHandler messageHandler; + + private final NoticeStatusService noticeStatusService; + + private final UserService userService; + + /** + * 获取通知公告分页列表 + * + * @param queryParams 查询参数 + * @return {@link IPage} 通知公告分页列表 + */ + @Override + public IPage getNoticePage(NoticeQuery queryParams) { + Page noticePage = this.baseMapper.getNoticePage( + new Page<>(queryParams.getPageNum(), queryParams.getPageSize()), + queryParams + ); + return noticeConverter.toPageVo(noticePage); + } + + /** + * 获取通知公告表单数据 + * + * @param id 通知公告ID + * @return {@link NoticeForm} 通知公告表单对象 + */ + @Override + public NoticeForm getNoticeFormData(Long id) { + Notice entity = this.getById(id); + return noticeConverter.toForm(entity); + } + + /** + * 新增通知公告 + * + * @param formData 通知公告表单对象 + * @return {@link Boolean} 是否新增成功 + */ + @Override + public boolean saveNotice(NoticeForm formData) { + Notice entity = noticeConverter.toEntity(formData); + entity.setReleaseStatus(0); + entity.setCreateBy(SecurityUtils.getUserId()); + if (entity.getTarType() == 1) { + Assert.notBlank(entity.getTarIds(), "指定用户不能为空"); + } + return this.save(entity); + } + + /** + * 更新通知公告 + * + * @param id 通知公告ID + * @param formData 通知公告表单对象 + * @return {@link Boolean} 是否更新成功 + */ + @Override + public boolean updateNotice(Long id, NoticeForm formData) { + Notice entity = noticeConverter.toEntity(formData); + entity.setUpdateBy(SecurityUtils.getUserId()); + if (entity.getTarType() == 1) { + Assert.notBlank(entity.getTarIds(), "指定用户不能为空"); + } + return this.updateById(entity); + } + + /** + * 删除通知公告 + * + * @param ids 通知公告ID,多个以英文逗号(,)分割 + * @return {@link Boolean} 是否删除成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteNotices(String ids) { + Assert.isTrue(StrUtil.isNotBlank(ids), "删除的通知公告数据为空"); + // 逻辑删除 + List idList = Arrays.stream(ids.split(SymbolConstant.COMMA)) + .map(Long::parseLong) + .toList(); + boolean b = this.removeByIds(idList); + if (b) { + //删除通知公告的同时,需要删除通知公告对应的用户通知状态 + noticeStatusService.remove(new LambdaQueryWrapper().in(NoticeStatus::getNoticeId, idList)); + } + + return true; + } + + /** + * 发布通知公告 + * @param id 通知公告ID + * @return 是否发布成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean releaseNotice(Long id) { + Notice notice = this.getById(id); + Assert.notNull(notice, "通知公告不存在"); + Assert.isTrue(notice.getReleaseStatus() != 1, "通知公告已发布"); + notice.setReleaseStatus(1); + notice.setReleaseBy(SecurityUtils.getUserId()); + notice.setReleaseTime(LocalDateTime.now()); + this.updateById(notice); + //发布通知公告的同时,需要将通知公告发送给目标用户 + //先删除掉该通知公告之前对应的用户信息 + noticeStatusService.remove(new LambdaQueryWrapper().eq(NoticeStatus::getNoticeId, id)); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + if (notice.getTarType() == 1) { + Assert.notBlank(notice.getTarIds(), "指定用户不能为空"); + queryWrapper.in(User::getId, Arrays.asList(notice.getTarIds().split(SymbolConstant.COMMA))); + } + //查询出目标用户,增加用户通知状态 + List list = userService.list(queryWrapper); + List needSaveList = list.stream().map(user -> { + NoticeStatus noticeStatus = new NoticeStatus(); + noticeStatus.setNoticeId(id); + noticeStatus.setUserId(user.getId()); + noticeStatus.setReadStatus(0); + return noticeStatus; + }).toList(); + if(needSaveList.size() > 0){ + noticeStatusService.saveBatch(needSaveList); + } + //最后,给当前在线的用户发送websocket消息 + List usernameList = null; + if(notice.getTarType() == 1){ + List collect = needSaveList.stream().map(NoticeStatus::getUserId).collect(Collectors.toList()); + List userList = userService.list(new LambdaQueryWrapper().in(User::getId, collect).select(User::getUsername)); + usernameList = userList.stream().map(User::getUsername).collect(Collectors.toList()); + } + MessageDTO message = new MessageDTO(); + message.setNoticeWay(NoticeWayEnum.WEBSOCKET); + message.setReceiver(usernameList); + message.setContent(getNoticeContent(notice)); + message.setSender(SecurityUtils.getUsername()); + messageHandler.sendMessage(message); + return this.updateById(notice); + } + + /** + * 自定义组合公告内容 + * + * @param notice 通知公告 + * @return 自定义组合通知公告内容 + */ + private String getNoticeContent(Notice notice) { + JSONObject jsonObject = new JSONObject(); + jsonObject.set("id", notice.getId()); + jsonObject.set("title", notice.getTitle()); + jsonObject.set("messageType", notice.getNoticeType()); + jsonObject.set("releaseTime", notice.getReleaseTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + jsonObject.set("type", "release"); + return jsonObject.toString(); + } + + /** + * 撤回通知公告 + * + * @param id 通知公告ID + * @return 是否撤回成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean recallNotice(Long id) { + Notice notice = this.getById(id); + Assert.notNull(notice, "通知公告不存在"); + Assert.isTrue(notice.getReleaseStatus() == 1, "通知公告未发布"); + notice.setReleaseStatus(2); + notice.setRecallTime(LocalDateTime.now()); + if (!this.updateById(notice)) { + return false; + } + //先删除掉该通知公告之前对应的用户信息 + noticeStatusService.remove(new LambdaQueryWrapper().eq(NoticeStatus::getNoticeId, id)); + return true; + } + + /** + * 阅读通知公告 + * @param id 通知公告ID + * @return 通知公告表单对象 + */ + @Override + public NoticeDetailVO readNotice(Long id) { + NoticeDetailVO noticeDetailVO = this.getReadNoticeDetail(id); + Assert.isTrue(noticeDetailVO != null && noticeDetailVO.getReleaseStatus() == 1, "公告不存在或未发布"); + //获取当前登录用户 + Long userId = SecurityUtils.getUserId(); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper() + .eq(NoticeStatus::getUserId, userId) + .eq(NoticeStatus::getNoticeId, id) + .eq(NoticeStatus::getReadStatus, 0); + NoticeStatus noticeStatus = noticeStatusService.getOne(queryWrapper); + if (noticeStatus != null) { + noticeStatus.setReadStatus(1); + noticeStatusService.updateById(noticeStatus); + } + return noticeDetailVO; + } + + /** + * 获取阅读时通知公告详情 + * @param id 通知公告ID + * @return + */ + @Override + public NoticeDetailVO getReadNoticeDetail(Long id) { + Assert.notNull(id, "公告ID不能为空"); + NoticeDetailVO noticeDetailVO = this.baseMapper.getReadNoticeVO(id); + Assert.isTrue(noticeDetailVO != null, "公告不存在"); + return noticeDetailVO; + } + + /** + * 获取当前登录用户的通知公告列表 + * @param queryParams 查询参数 + * @return 通知公告分页列表 + */ + @Override + public IPage getMyNoticePage(NoticeQuery queryParams) { + Long userId = SecurityUtils.getUserId(); + queryParams.setUserId(userId); + return noticeStatusService.getMyNoticePage(new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),queryParams); + } + +} diff --git a/src/main/java/com/youlai/boot/system/service/impl/NoticeStatusServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/NoticeStatusServiceImpl.java new file mode 100644 index 00000000..2a1599bd --- /dev/null +++ b/src/main/java/com/youlai/boot/system/service/impl/NoticeStatusServiceImpl.java @@ -0,0 +1,67 @@ +package com.youlai.boot.system.service.impl; + +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.youlai.boot.core.security.util.SecurityUtils; +import com.youlai.boot.system.mapper.NoticeStatusMapper; +import com.youlai.boot.system.model.entity.NoticeStatus; +import com.youlai.boot.system.model.query.NoticeQuery; +import com.youlai.boot.system.model.vo.NoticeStatusVO; +import com.youlai.boot.system.model.vo.NoticeVO; +import com.youlai.boot.system.service.NoticeStatusService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 用户公告状态服务实现类 + * + * @author youlaitech + * @since 2024-08-28 16:56 + */ +@Service +@RequiredArgsConstructor +public class NoticeStatusServiceImpl extends ServiceImpl implements NoticeStatusService { + + private final NoticeStatusMapper noticeStatusMapper; + + /** + * 获取未读的通知公告 + * @return 公告列表 + */ + @Override + public List listUnreadNotices() { + //获取当前登录用户 + Long userId = SecurityUtils.getUserId(); + return noticeStatusMapper.listUnreadNotices(userId); + } + + /** + * 全部标记为已读 + * @return 是否成功 + */ + @Override + public boolean readAll() { + Long userId = SecurityUtils.getUserId(); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.eq(NoticeStatus::getUserId, userId); + updateWrapper.set(NoticeStatus::getReadStatus, 1); + return this.update(updateWrapper); + } + + /** + * 分页获取我的通知公告 + * @param page 分页对象 + * @param queryParams 查询参数 + * @return 通知公告分页列表 + */ + @Override + public IPage getMyNoticePage(Page page, NoticeQuery queryParams) { + return this.getBaseMapper().getMyNoticePage(new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),queryParams); + } + + +} diff --git a/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java b/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java index b055b289..a1f7b8f1 100644 --- a/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java +++ b/src/main/java/com/youlai/boot/system/service/impl/UserServiceImpl.java @@ -11,6 +11,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.youlai.boot.common.constant.RedisConstants; import com.youlai.boot.common.constant.SystemConstants; import com.youlai.boot.common.enums.ContactType; +import com.youlai.boot.common.model.Option; import com.youlai.boot.module.mail.service.MailService; import com.youlai.boot.module.sms.service.SmsService; import com.youlai.boot.system.model.entity.User; @@ -39,6 +40,7 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; @@ -419,4 +421,18 @@ public class UserServiceImpl extends ServiceImpl implements Us .set(User::getEmail, email) ); } + + /** + * 获取用户选项列表 + * + * @return {@link List>} 用户选项列表 + */ + @Override + public List> listUserOptions() { + List list = this.list(); + if (CollectionUtil.isNotEmpty(list)) { + return list.stream().map(user -> new Option<>(user.getId().toString(), user.getNickname())).collect(Collectors.toList()); + } + return Collections.emptyList(); + } } diff --git a/src/main/resources/mapper/NoticeMapper.xml b/src/main/resources/mapper/NoticeMapper.xml new file mode 100644 index 00000000..ef23eaee --- /dev/null +++ b/src/main/resources/mapper/NoticeMapper.xml @@ -0,0 +1,63 @@ + + + + + + + + + + diff --git a/src/main/resources/mapper/NoticeStatusMapper.xml b/src/main/resources/mapper/NoticeStatusMapper.xml new file mode 100644 index 00000000..76b706e9 --- /dev/null +++ b/src/main/resources/mapper/NoticeStatusMapper.xml @@ -0,0 +1,49 @@ + + + + + + + + diff --git a/src/main/resources/templates/codegen/index.vue.vm b/src/main/resources/templates/codegen/index.vue.vm index 39d203ff..b8c9529e 100644 --- a/src/main/resources/templates/codegen/index.vue.vm +++ b/src/main/resources/templates/codegen/index.vue.vm @@ -122,7 +122,8 @@ key="$fieldConfig.fieldName" label="$fieldConfig.fieldComment" prop="$fieldConfig.fieldName" - min-width="100" + min-width="150" + align="center" /> #end #end