From 0f3e94f32fcf8be90549b90e5b9b853bd63079a5 Mon Sep 17 00:00:00 2001 From: Charles7c Date: Tue, 20 May 2025 22:35:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E5=85=AC=E5=91=8A?= =?UTF-8?q?=E5=8F=8A=E6=B6=88=E6=81=AF=EF=BC=8C=E5=85=AC=E5=91=8A=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E7=B3=BB=E7=BB=9F=E6=B6=88=E6=81=AF=E6=8E=A8=E9=80=81?= =?UTF-8?q?=E6=8F=90=E9=86=92=E3=80=81=E5=AE=9A=E6=97=B6=E5=8F=91=E5=B8=83?= =?UTF-8?q?=E3=80=81=E7=BD=AE=E9=A1=B6=E3=80=81=E8=AE=B0=E5=BD=95=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../db/changelog/mysql/snail-job_data.sql | 5 + .../changelog/postgresql/snail-job_data.sql | 5 + .../auth/handler/SocialLoginHandler.java | 18 +-- .../system/enums/MessageTemplateEnum.java | 8 +- .../admin/system/enums/MessageTypeEnum.java | 7 +- .../admin/system/enums/NoticeMethodEnum.java | 40 ++++++ .../admin/system/enums/NoticeStatusEnum.java | 34 +---- ...eUserMapper.java => MessageLogMapper.java} | 17 +-- .../admin/system/mapper/MessageMapper.java | 34 +++-- .../admin/system/mapper/NoticeLogMapper.java | 29 ++++ .../admin/system/mapper/NoticeMapper.java | 28 ++-- .../admin/system/model/entity/MessageDO.java | 21 ++- .../{MessageUserDO.java => MessageLogDO.java} | 20 +-- .../admin/system/model/entity/NoticeDO.java | 39 ++++-- .../system/model/entity/NoticeLogDO.java | 61 ++++++++ .../system/model/query/MessageQuery.java | 9 +- .../admin/system/model/query/NoticeQuery.java | 10 +- .../admin/system/model/req/MessageReq.java | 14 +- .../admin/system/model/req/NoticeReq.java | 57 +++++--- .../resp/dashboard/DashboardNoticeResp.java | 6 + .../model/resp/message/MessageResp.java | 24 +--- .../resp/message/MessageTypeUnreadResp.java | 2 +- .../resp/{ => notice}/NoticeDetailResp.java | 86 +++++++----- .../model/resp/{ => notice}/NoticeResp.java | 64 +++++---- .../model/resp/notice/NoticeUnreadResp.java | 49 +++++++ ...serService.java => MessageLogService.java} | 29 ++-- .../admin/system/service/MessageService.java | 21 ++- .../system/service/NoticeLogService.java | 52 +++++++ .../admin/system/service/NoticeService.java | 28 +++- .../service/impl/MessageLogServiceImpl.java | 71 ++++++++++ .../service/impl/MessageServiceImpl.java | 72 ++++++++-- .../service/impl/MessageUserServiceImpl.java | 98 ------------- .../service/impl/NoticeLogServiceImpl.java | 82 +++++++++++ .../service/impl/NoticeServiceImpl.java | 132 +++++++++++++++++- .../resources/mapper/MessageLogMapper.xml | 4 + .../main/resources/mapper/MessageMapper.xml | 56 +++++++- .../resources/mapper/MessageUserMapper.xml | 14 -- .../main/resources/mapper/NoticeMapper.xml | 61 +++++--- .../controller/system/MessageController.java | 84 ----------- .../controller/system/NoticeController.java | 24 ++-- .../system/UserMessageController.java | 59 +++++++- .../schedule => job}/DemoEnvironmentJob.java | 8 +- .../continew/admin/job/NoticePublishJob.java | 112 +++++++++++++++ .../db/changelog/mysql/main_data.sql | 34 +++-- .../db/changelog/mysql/main_table.sql | 36 +++-- .../db/changelog/postgresql/main_data.sql | 34 +++-- .../db/changelog/postgresql/main_table.sql | 53 ++++--- 47 files changed, 1288 insertions(+), 563 deletions(-) create mode 100644 continew-module-system/src/main/java/top/continew/admin/system/enums/NoticeMethodEnum.java rename continew-module-system/src/main/java/top/continew/admin/system/mapper/{MessageUserMapper.java => MessageLogMapper.java} (61%) create mode 100644 continew-module-system/src/main/java/top/continew/admin/system/mapper/NoticeLogMapper.java rename continew-module-system/src/main/java/top/continew/admin/system/model/entity/{MessageUserDO.java => MessageLogDO.java} (76%) create mode 100644 continew-module-system/src/main/java/top/continew/admin/system/model/entity/NoticeLogDO.java rename continew-module-system/src/main/java/top/continew/admin/system/model/resp/{ => notice}/NoticeDetailResp.java (67%) rename continew-module-system/src/main/java/top/continew/admin/system/model/resp/{ => notice}/NoticeResp.java (65%) create mode 100644 continew-module-system/src/main/java/top/continew/admin/system/model/resp/notice/NoticeUnreadResp.java rename continew-module-system/src/main/java/top/continew/admin/system/service/{MessageUserService.java => MessageLogService.java} (59%) create mode 100644 continew-module-system/src/main/java/top/continew/admin/system/service/NoticeLogService.java create mode 100644 continew-module-system/src/main/java/top/continew/admin/system/service/impl/MessageLogServiceImpl.java delete mode 100644 continew-module-system/src/main/java/top/continew/admin/system/service/impl/MessageUserServiceImpl.java create mode 100644 continew-module-system/src/main/java/top/continew/admin/system/service/impl/NoticeLogServiceImpl.java create mode 100644 continew-module-system/src/main/resources/mapper/MessageLogMapper.xml delete mode 100644 continew-module-system/src/main/resources/mapper/MessageUserMapper.xml delete mode 100644 continew-webapi/src/main/java/top/continew/admin/controller/system/MessageController.java rename continew-webapi/src/main/java/top/continew/admin/{controller/schedule => job}/DemoEnvironmentJob.java (96%) create mode 100644 continew-webapi/src/main/java/top/continew/admin/job/NoticePublishJob.java diff --git a/continew-extension/continew-extension-schedule-server/src/main/resources/db/changelog/mysql/snail-job_data.sql b/continew-extension/continew-extension-schedule-server/src/main/resources/db/changelog/mysql/snail-job_data.sql index 7c8953d2..00cbc4e8 100644 --- a/continew-extension/continew-extension-schedule-server/src/main/resources/db/changelog/mysql/snail-job_data.sql +++ b/continew-extension/continew-extension-schedule-server/src/main/resources/db/changelog/mysql/snail-job_data.sql @@ -12,3 +12,8 @@ VALUES ('admin', '465c194afb65670f38322df087f0a9bb225cc257e43eb4ac5a0c98ef5b3173 -- 默认分组:continew-admin INSERT INTO `sj_group_config` (`id`, `namespace_id`, `group_name`, `description`, `token`, `group_status`, `version`, `group_partition`, `id_generator_mode`, `init_scene`, `create_dt`, `update_dt`) VALUES (1, '764d604ec6fc45f68cd92514c40e9e1a', 'continew-admin', '默认分组', 'SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj', 1, 1, 0, 2, 1, NOW(), NOW()); + +-- 默认任务:NoticePublishJob +INSERT INTO `sj_job` +(`namespace_id`, `group_name`, `job_name`, `args_type`, `next_trigger_at`, `job_status`, `task_type`, `route_key`, `executor_type`, `executor_info`, `trigger_type`, `trigger_interval`, `block_strategy`, `executor_timeout`, `max_retry_times`, `parallel_num`, `retry_interval`, `bucket_index`, `resident`, `notify_ids`, `owner_id`, `description`, `ext_attrs`, `deleted`, `create_dt`, `update_dt`) +VALUES ('764d604ec6fc45f68cd92514c40e9e1a', 'continew-admin', '公告发布', 1, 1747546500000, 1, 1, 4, 1, 'NoticePublishJob', 3, '0 * * * * ?', 1, 60, 3, 1, 1, 27, 0, '', NULL, '定时发布公告', '', 0, NOW(), NOW()); diff --git a/continew-extension/continew-extension-schedule-server/src/main/resources/db/changelog/postgresql/snail-job_data.sql b/continew-extension/continew-extension-schedule-server/src/main/resources/db/changelog/postgresql/snail-job_data.sql index 313279d6..5d3e2bb0 100644 --- a/continew-extension/continew-extension-schedule-server/src/main/resources/db/changelog/postgresql/snail-job_data.sql +++ b/continew-extension/continew-extension-schedule-server/src/main/resources/db/changelog/postgresql/snail-job_data.sql @@ -12,3 +12,8 @@ VALUES ('admin', '465c194afb65670f38322df087f0a9bb225cc257e43eb4ac5a0c98ef5b3173 -- 默认分组:continew-admin INSERT INTO sj_group_config (id, namespace_id, group_name, description, token, group_status, version, group_partition, id_generator_mode, init_scene, create_dt, update_dt) VALUES (1, '764d604ec6fc45f68cd92514c40e9e1a', 'continew-admin', '默认分组', 'SJ_Wyz3dmsdbDOkDujOTSSoBjGQP1BMsVnj', 1, 1, 0, 2, 1, NOW(), NOW()); + +-- 默认任务:NoticePublishJob +INSERT INTO sj_job +(namespace_id, group_name, job_name, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy, executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, description, ext_attrs, deleted, create_dt, update_dt) +VALUES ('764d604ec6fc45f68cd92514c40e9e1a', 'continew-admin', '公告发布', 1, 1747546500000, 1, 1, 4, 1, 'NoticePublishJob', 3, '0 * * * * ?', 1, 60, 3, 1, 1, 27, 0, '', NULL, '定时发布公告', '', 0, NOW(), NOW()); \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/auth/handler/SocialLoginHandler.java b/continew-module-system/src/main/java/top/continew/admin/auth/handler/SocialLoginHandler.java index f1d1b1f9..8358a39f 100644 --- a/continew-module-system/src/main/java/top/continew/admin/auth/handler/SocialLoginHandler.java +++ b/continew-module-system/src/main/java/top/continew/admin/auth/handler/SocialLoginHandler.java @@ -24,7 +24,6 @@ import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.ReUtil; import cn.hutool.json.JSONUtil; import com.xkcoding.justauth.autoconfigure.JustAuthProperties; - import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import me.zhyd.oauth.AuthRequestBuilder; @@ -54,11 +53,9 @@ import top.continew.admin.system.service.UserSocialService; import top.continew.starter.core.autoconfigure.project.ProjectProperties; import top.continew.starter.core.exception.BadRequestException; import top.continew.starter.core.validation.ValidationUtils; -import top.continew.starter.messaging.websocket.util.WebSocketUtils; import java.time.LocalDateTime; import java.util.Collections; -import java.util.List; import java.util.Objects; /** @@ -168,15 +165,10 @@ public class SocialLoginHandler extends AbstractLoginHandler { * @param user 用户信息 */ private void sendSecurityMsg(UserDO user) { - MessageReq req = new MessageReq(); - MessageTemplateEnum socialRegister = MessageTemplateEnum.SOCIAL_REGISTER; - req.setTitle(socialRegister.getTitle().formatted(projectProperties.getName())); - req.setContent(socialRegister.getContent().formatted(user.getNickname())); - req.setType(MessageTypeEnum.SECURITY); - messageService.add(req, CollUtil.toList(user.getId())); - List tokenList = StpUtil.getTokenValueListByLoginId(user.getId()); - for (String token : tokenList) { - WebSocketUtils.sendMessage(token, "1"); - } + MessageTemplateEnum template = MessageTemplateEnum.SOCIAL_REGISTER; + MessageReq req = new MessageReq(MessageTypeEnum.SECURITY); + req.setTitle(template.getTitle().formatted(projectProperties.getName())); + req.setContent(template.getContent().formatted(user.getNickname())); + messageService.add(req, CollUtil.toList(user.getId().toString())); } } diff --git a/continew-module-system/src/main/java/top/continew/admin/system/enums/MessageTemplateEnum.java b/continew-module-system/src/main/java/top/continew/admin/system/enums/MessageTemplateEnum.java index ef61af58..2ccd8b7a 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/enums/MessageTemplateEnum.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/enums/MessageTemplateEnum.java @@ -32,8 +32,14 @@ public enum MessageTemplateEnum { /** * 第三方登录 */ - SOCIAL_REGISTER("欢迎注册 %s", "尊敬的 %s,欢迎注册使用,请及时配置您的密码。"); + SOCIAL_REGISTER("欢迎注册 %s", "尊敬的 %s,欢迎注册使用,请及时配置您的密码。", "/user/profile"), + + /** + * 公告发布 + */ + NOTICE_PUBLISH("您有一条新的公告", "公告《%s》已发布,请及时查看。", "/user/notice?id=%s"); private final String title; private final String content; + private final String path; } diff --git a/continew-module-system/src/main/java/top/continew/admin/system/enums/MessageTypeEnum.java b/continew-module-system/src/main/java/top/continew/admin/system/enums/MessageTypeEnum.java index b1d9b658..aeface9f 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/enums/MessageTypeEnum.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/enums/MessageTypeEnum.java @@ -31,10 +31,15 @@ import top.continew.starter.core.enums.BaseEnum; @RequiredArgsConstructor public enum MessageTypeEnum implements BaseEnum { + /** + * 系统消息 + */ + SYSTEM(1, "系统消息", UiConstants.COLOR_PRIMARY), + /** * 安全消息 */ - SECURITY(1, "安全消息", UiConstants.COLOR_PRIMARY),; + SECURITY(2, "安全消息", UiConstants.COLOR_WARNING),; private final Integer value; private final String description; diff --git a/continew-module-system/src/main/java/top/continew/admin/system/enums/NoticeMethodEnum.java b/continew-module-system/src/main/java/top/continew/admin/system/enums/NoticeMethodEnum.java new file mode 100644 index 00000000..b58ce148 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/enums/NoticeMethodEnum.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.enums; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import top.continew.starter.core.enums.BaseEnum; + +/** + * 公告通知方式枚举 + * + * @author Charles7c + * @since 2025/5/8 21:18 + */ +@Getter +@RequiredArgsConstructor +public enum NoticeMethodEnum implements BaseEnum { + + /** + * 系统消息 + */ + SYSTEM_MESSAGE(1, "系统消息"),; + + private final Integer value; + private final String description; +} diff --git a/continew-module-system/src/main/java/top/continew/admin/system/enums/NoticeStatusEnum.java b/continew-module-system/src/main/java/top/continew/admin/system/enums/NoticeStatusEnum.java index 704ebe71..ddd92b7d 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/enums/NoticeStatusEnum.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/enums/NoticeStatusEnum.java @@ -21,8 +21,6 @@ import lombok.RequiredArgsConstructor; import top.continew.admin.common.constant.UiConstants; import top.continew.starter.core.enums.BaseEnum; -import java.time.LocalDateTime; - /** * 公告状态枚举 * @@ -33,40 +31,22 @@ import java.time.LocalDateTime; @RequiredArgsConstructor public enum NoticeStatusEnum implements BaseEnum { + /** + * 草稿 + */ + DRAFT(1, "草稿", UiConstants.COLOR_WARNING), + /** * 待发布 */ - PENDING_RELEASE(1, "待发布", UiConstants.COLOR_PRIMARY), + PENDING(2, "待发布", UiConstants.COLOR_PRIMARY), /** * 已发布 */ - PUBLISHED(2, "已发布", UiConstants.COLOR_SUCCESS), - - /** - * 已过期 - */ - EXPIRED(3, "已过期", UiConstants.COLOR_ERROR),; + PUBLISHED(3, "已发布", UiConstants.COLOR_SUCCESS),; private final Integer value; private final String description; private final String color; - - /** - * 获取公告状态 - * - * @param effectiveTime 生效时间 - * @param terminateTime 终止时间 - * @return 公告状态 - */ - public static NoticeStatusEnum getStatus(LocalDateTime effectiveTime, LocalDateTime terminateTime) { - LocalDateTime now = LocalDateTime.now(); - if (effectiveTime != null && effectiveTime.isAfter(now)) { - return PENDING_RELEASE; - } - if (terminateTime != null && terminateTime.isBefore(now)) { - return EXPIRED; - } - return PUBLISHED; - } } diff --git a/continew-module-system/src/main/java/top/continew/admin/system/mapper/MessageUserMapper.java b/continew-module-system/src/main/java/top/continew/admin/system/mapper/MessageLogMapper.java similarity index 61% rename from continew-module-system/src/main/java/top/continew/admin/system/mapper/MessageUserMapper.java rename to continew-module-system/src/main/java/top/continew/admin/system/mapper/MessageLogMapper.java index 330a7aeb..b56dcdaf 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/mapper/MessageUserMapper.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/mapper/MessageLogMapper.java @@ -16,24 +16,15 @@ package top.continew.admin.system.mapper; -import org.apache.ibatis.annotations.Param; -import top.continew.admin.system.model.entity.MessageUserDO; +import top.continew.admin.system.model.entity.MessageLogDO; import top.continew.starter.data.mp.base.BaseMapper; /** - * 消息和用户 Mapper + * 消息日志 Mapper * * @author Bull-BCLS + * @author Charles7c * @since 2023/10/15 20:25 */ -public interface MessageUserMapper extends BaseMapper { - - /** - * 根据用户 ID 和消息类型查询未读消息数量 - * - * @param userId 用户 ID - * @param type 消息类型 - * @return 未读消息信息 - */ - Long selectUnreadCountByUserIdAndType(@Param("userId") Long userId, @Param("type") Integer type); +public interface MessageLogMapper extends BaseMapper { } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/mapper/MessageMapper.java b/continew-module-system/src/main/java/top/continew/admin/system/mapper/MessageMapper.java index 31a2bba2..24a089cc 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/mapper/MessageMapper.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/mapper/MessageMapper.java @@ -16,14 +16,16 @@ package top.continew.admin.system.mapper; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; -import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.apache.ibatis.annotations.Param; import top.continew.admin.system.model.entity.MessageDO; +import top.continew.admin.system.model.query.MessageQuery; import top.continew.admin.system.model.resp.message.MessageResp; import top.continew.starter.data.mp.base.BaseMapper; +import java.util.List; + /** * 消息 Mapper * @@ -33,12 +35,28 @@ import top.continew.starter.data.mp.base.BaseMapper; public interface MessageMapper extends BaseMapper { /** - * 分页查询列表 + * 分页查询消息列表 * - * @param page 分页查询条件 - * @param queryWrapper 查询条件 - * @return 分页信息 + * @param page 分页参数 + * @param query 查询条件 + * @return 消息列表 */ - IPage selectPageByUserId(@Param("page") IPage page, - @Param(Constants.WRAPPER) QueryWrapper queryWrapper); + IPage selectMessagePage(@Param("page") Page page, @Param("query") MessageQuery query); + + /** + * 查询未读消息列表 + * + * @param userId 用户 ID + * @return 消息列表 + */ + List selectUnreadListByUserId(@Param("userId") Long userId); + + /** + * 查询未读消息数量 + * + * @param userId 用户 ID + * @param type 消息类型 + * @return 未读消息数量 + */ + Long selectUnreadCountByUserIdAndType(@Param("userId") Long userId, @Param("type") Integer type); } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/mapper/NoticeLogMapper.java b/continew-module-system/src/main/java/top/continew/admin/system/mapper/NoticeLogMapper.java new file mode 100644 index 00000000..dc257589 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/mapper/NoticeLogMapper.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.mapper; + +import top.continew.admin.system.model.entity.NoticeLogDO; +import top.continew.starter.data.mp.base.BaseMapper; + +/** + * 公告日志 Mapper + * + * @author Charles7c + * @since 2025/5/18 19:17 + */ +public interface NoticeLogMapper extends BaseMapper { +} diff --git a/continew-module-system/src/main/java/top/continew/admin/system/mapper/NoticeMapper.java b/continew-module-system/src/main/java/top/continew/admin/system/mapper/NoticeMapper.java index 71aea2f9..599d6a75 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/mapper/NoticeMapper.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/mapper/NoticeMapper.java @@ -21,8 +21,8 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.apache.ibatis.annotations.Param; import top.continew.admin.system.model.entity.NoticeDO; import top.continew.admin.system.model.query.NoticeQuery; -import top.continew.admin.system.model.resp.NoticeDetailResp; import top.continew.admin.system.model.resp.dashboard.DashboardNoticeResp; +import top.continew.admin.system.model.resp.notice.NoticeResp; import top.continew.starter.data.mp.base.BaseMapper; import java.util.List; @@ -35,14 +35,6 @@ import java.util.List; */ public interface NoticeMapper extends BaseMapper { - /** - * 查询仪表盘公告列表 - * - * @param userId 用户 ID - * @return 仪表盘公告列表 - */ - List selectDashboardList(@Param("userId") Long userId); - /** * 分页查询公告列表 * @@ -50,5 +42,21 @@ public interface NoticeMapper extends BaseMapper { * @param query 查询条件 * @return 公告列表 */ - IPage selectNoticePage(@Param("page") Page page, @Param("query") NoticeQuery query); + IPage selectNoticePage(@Param("page") Page page, @Param("query") NoticeQuery query); + + /** + * 查询未读公告数量 + * + * @param userId 用户 ID + * @return 未读公告数量 + */ + Long selectUnreadCountByUserId(@Param("userId") Long userId); + + /** + * 查询仪表盘公告列表 + * + * @param userId 用户 ID + * @return 仪表盘公告列表 + */ + List selectDashboardList(@Param("userId") Long userId); } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/MessageDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/MessageDO.java index 1e0157a6..8567ad90 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/MessageDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/MessageDO.java @@ -20,12 +20,15 @@ import com.baomidou.mybatisplus.annotation.FieldFill; 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.Data; import top.continew.admin.system.enums.MessageTypeEnum; +import top.continew.admin.system.enums.NoticeScopeEnum; import java.io.Serial; import java.io.Serializable; import java.time.LocalDateTime; +import java.util.List; /** * 消息实体 @@ -57,15 +60,25 @@ public class MessageDO implements Serializable { private String content; /** - * 类型(1:系统消息) + * 类型 */ private MessageTypeEnum type; /** - * 创建人 + * 跳转路径 */ - @TableField(fill = FieldFill.INSERT) - private Long createUser; + private String path; + + /** + * 通知范围 + */ + private NoticeScopeEnum scope; + + /** + * 通知用户 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List users; /** * 创建时间 diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/MessageUserDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/MessageLogDO.java similarity index 76% rename from continew-module-system/src/main/java/top/continew/admin/system/model/entity/MessageUserDO.java rename to continew-module-system/src/main/java/top/continew/admin/system/model/entity/MessageLogDO.java index 6c47f271..6cb04e45 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/MessageUserDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/MessageLogDO.java @@ -18,20 +18,23 @@ package top.continew.admin.system.model.entity; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; +import lombok.NoArgsConstructor; import java.io.Serial; import java.io.Serializable; import java.time.LocalDateTime; /** - * 消息和用户关联实体 + * 消息日志实体 * + * @author Charles7c * @author Bull-BCLS * @since 2023/10/15 20:25 */ @Data -@TableName("sys_message_user") -public class MessageUserDO implements Serializable { +@NoArgsConstructor +@TableName("sys_message_log") +public class MessageLogDO implements Serializable { @Serial private static final long serialVersionUID = 1L; @@ -46,13 +49,14 @@ public class MessageUserDO implements Serializable { */ private Long userId; - /** - * 是否已读 - */ - private Boolean isRead; - /** * 读取时间 */ private LocalDateTime readTime; + + public MessageLogDO(Long messageId, Long userId, LocalDateTime readTime) { + this.messageId = messageId; + this.userId = userId; + this.readTime = readTime; + } } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/NoticeDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/NoticeDO.java index 98842c74..3d30ffcc 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/NoticeDO.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/NoticeDO.java @@ -22,6 +22,7 @@ import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.Data; import top.continew.admin.common.model.entity.BaseDO; import top.continew.admin.system.enums.NoticeScopeEnum; +import top.continew.admin.system.enums.NoticeStatusEnum; import java.io.Serial; import java.time.LocalDateTime; @@ -51,20 +52,10 @@ public class NoticeDO extends BaseDO { private String content; /** - * 类型 + * 分类(取值于字典 notice_type) */ private String type; - /** - * 生效时间 - */ - private LocalDateTime effectiveTime; - - /** - * 终止时间 - */ - private LocalDateTime terminateTime; - /** * 通知范围 */ @@ -75,4 +66,30 @@ public class NoticeDO extends BaseDO { */ @TableField(typeHandler = JacksonTypeHandler.class) private List noticeUsers; + + /** + * 通知方式 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List noticeMethods; + + /** + * 是否定时 + */ + private Boolean isTiming; + + /** + * 发布时间 + */ + private LocalDateTime publishTime; + + /** + * 是否置顶 + */ + private Boolean isTop; + + /** + * 状态 + */ + private NoticeStatusEnum status; } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/NoticeLogDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/NoticeLogDO.java new file mode 100644 index 00000000..474229a6 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/NoticeLogDO.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.model.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 公告日志实体 + * + * @author Charles7c + * @since 2025/5/18 19:16 + */ +@Data +@NoArgsConstructor +@TableName("sys_notice_log") +public class NoticeLogDO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 公告 ID + */ + private Long noticeId; + + /** + * 用户 ID + */ + private Long userId; + + /** + * 读取时间 + */ + private LocalDateTime readTime; + + public NoticeLogDO(Long noticeId, Long userId, LocalDateTime readTime) { + this.noticeId = noticeId; + this.userId = userId; + this.readTime = readTime; + } +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/query/MessageQuery.java b/continew-module-system/src/main/java/top/continew/admin/system/model/query/MessageQuery.java index 130fb253..8a1f2581 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/query/MessageQuery.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/query/MessageQuery.java @@ -18,10 +18,6 @@ package top.continew.admin.system.model.query; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import top.continew.admin.system.enums.MessageTypeEnum; -import top.continew.starter.data.core.annotation.Query; -import top.continew.starter.data.core.annotation.QueryIgnore; -import top.continew.starter.data.core.enums.QueryType; import java.io.Serial; import java.io.Serializable; @@ -49,26 +45,23 @@ public class MessageQuery implements Serializable { * 标题 */ @Schema(description = "标题", example = "欢迎注册 xxx") - @Query(type = QueryType.LIKE) private String title; /** * 类型 */ @Schema(description = "类型", example = "1") - private MessageTypeEnum type; + private Integer type; /** * 是否已读 */ @Schema(description = "是否已读", example = "true") - @QueryIgnore private Boolean isRead; /** * 用户 ID */ @Schema(hidden = true) - @QueryIgnore private Long userId; } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/query/NoticeQuery.java b/continew-module-system/src/main/java/top/continew/admin/system/model/query/NoticeQuery.java index 1b0432cc..fbcceb1e 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/query/NoticeQuery.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/query/NoticeQuery.java @@ -16,11 +16,8 @@ package top.continew.admin.system.model.query; -import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import top.continew.starter.data.core.annotation.Query; -import top.continew.starter.data.core.enums.QueryType; import java.io.Serial; import java.io.Serializable; @@ -42,18 +39,17 @@ public class NoticeQuery implements Serializable { * 标题 */ @Schema(description = "标题", example = "这是公告标题") - @Query(type = QueryType.LIKE) private String title; /** - * 类型 + * 分类(取值于字典 notice_type) */ - @Schema(description = "类型", example = "1") + @Schema(description = "分类(取值于字典 notice_type)", example = "1") private String type; /** * 用户 ID */ - @JsonIgnore + @Schema(hidden = true) private Long userId; } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/req/MessageReq.java b/continew-module-system/src/main/java/top/continew/admin/system/model/req/MessageReq.java index cc3384a9..52b6b708 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/req/MessageReq.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/req/MessageReq.java @@ -20,6 +20,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; +import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; import top.continew.admin.system.enums.MessageTypeEnum; @@ -33,6 +34,7 @@ import java.io.Serializable; * @since 2023/10/15 19:05 */ @Data +@NoArgsConstructor @Schema(description = "消息创建请求参数") public class MessageReq implements Serializable { @@ -58,7 +60,17 @@ public class MessageReq implements Serializable { /** * 类型 */ - @Schema(description = "类型(1:系统消息)", example = "1") + @Schema(description = "类型", example = "1") @NotNull(message = "类型无效") private MessageTypeEnum type; + + /** + * 跳转路径 + */ + @Schema(description = "跳转路径", example = "/user/profile") + private String path; + + public MessageReq(MessageTypeEnum type) { + this.type = type; + } } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/req/NoticeReq.java b/continew-module-system/src/main/java/top/continew/admin/system/model/req/NoticeReq.java index fc5d3f76..57aca118 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/req/NoticeReq.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/req/NoticeReq.java @@ -17,12 +17,12 @@ package top.continew.admin.system.model.req; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Future; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import lombok.Data; import org.hibernate.validator.constraints.Length; import top.continew.admin.system.enums.NoticeScopeEnum; +import top.continew.admin.system.enums.NoticeStatusEnum; import java.io.Serial; import java.io.Serializable; @@ -58,26 +58,13 @@ public class NoticeReq implements Serializable { private String content; /** - * 类型(取值于字典 notice_type) + * 分类(取值于字典 notice_type) */ - @Schema(description = "类型(取值于字典 notice_type)", example = "1") - @NotBlank(message = "类型不能为空") - @Length(max = 30, message = "类型长度不能超过 {max} 个字符") + @Schema(description = "分类(取值于字典 notice_type)", example = "1") + @NotBlank(message = "分类不能为空") + @Length(max = 30, message = "分类长度不能超过 {max} 个字符") private String type; - /** - * 生效时间 - */ - @Schema(description = "生效时间", example = "2023-08-08 00:00:00", type = "string") - private LocalDateTime effectiveTime; - - /** - * 终止时间 - */ - @Schema(description = "终止时间", example = "2023-08-08 23:59:59", type = "string") - @Future(message = "终止时间必须是未来时间") - private LocalDateTime terminateTime; - /** * 通知范围 */ @@ -86,8 +73,38 @@ public class NoticeReq implements Serializable { private NoticeScopeEnum noticeScope; /** - * 指定用户 + * 通知用户 */ - @Schema(description = "指定用户", example = "[1,2,3]") + @Schema(description = "通知用户", example = "[1,2,3]") private List noticeUsers; + + /** + * 通知方式 + */ + @Schema(description = "通知方式", example = "[1,2]") + private List noticeMethods; + + /** + * 是否定时 + */ + @Schema(description = "是否定时", example = "false") + private Boolean isTiming; + + /** + * 发布时间 + */ + @Schema(description = "发布时间", example = "2023-08-08 00:00:00", type = "string") + private LocalDateTime publishTime; + + /** + * 是否置顶 + */ + @Schema(description = "是否置顶", example = "false") + private Boolean isTop; + + /** + * 状态 + */ + @Schema(description = "状态", example = "3") + private NoticeStatusEnum status; } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/dashboard/DashboardNoticeResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/dashboard/DashboardNoticeResp.java index 21bb7792..6678a507 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/dashboard/DashboardNoticeResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/dashboard/DashboardNoticeResp.java @@ -52,4 +52,10 @@ public class DashboardNoticeResp implements Serializable { */ @Schema(description = "类型(取值于字典 notice_type)", example = "1") private String type; + + /** + * 是否置顶 + */ + @Schema(description = "是否置顶", example = "false") + private Boolean isTop; } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/message/MessageResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/message/MessageResp.java index cc989f18..d35ac207 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/message/MessageResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/message/MessageResp.java @@ -16,11 +16,8 @@ package top.continew.admin.system.model.resp.message; -import cn.crane4j.annotation.Assemble; -import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -import top.continew.admin.common.constant.ContainerConstants; import top.continew.admin.system.enums.MessageTypeEnum; import java.io.Serial; @@ -61,9 +58,15 @@ public class MessageResp implements Serializable { /** * 类型 */ - @Schema(description = "类型(1:系统消息)", example = "1") + @Schema(description = "类型", example = "1") private MessageTypeEnum type; + /** + * 跳转路径 + */ + @Schema(description = "跳转路径", example = "/user/profile") + private String path; + /** * 是否已读 */ @@ -76,19 +79,6 @@ public class MessageResp implements Serializable { @Schema(description = "读取时间", example = "2023-08-08 23:59:59", type = "string") private LocalDateTime readTime; - /** - * 创建人 - */ - @JsonIgnore - @Assemble(prop = ":createUserString", container = ContainerConstants.USER_NICKNAME) - private Long createUser; - - /** - * 创建人 - */ - @Schema(description = "创建人", example = "超级管理员") - private String createUserString; - /** * 创建时间 */ diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/message/MessageTypeUnreadResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/message/MessageTypeUnreadResp.java index c1a3ba35..00aa84a2 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/message/MessageTypeUnreadResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/message/MessageTypeUnreadResp.java @@ -39,7 +39,7 @@ public class MessageTypeUnreadResp implements Serializable { /** * 类型 */ - @Schema(description = "类型(1:系统消息)", example = "1") + @Schema(description = "类型", example = "1") private MessageTypeEnum type; /** diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeDetailResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/notice/NoticeDetailResp.java similarity index 67% rename from continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeDetailResp.java rename to continew-module-system/src/main/java/top/continew/admin/system/model/resp/notice/NoticeDetailResp.java index 878af223..3dfffd9a 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeDetailResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/notice/NoticeDetailResp.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package top.continew.admin.system.model.resp; +package top.continew.admin.system.model.resp.notice; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; @@ -52,6 +52,14 @@ public class NoticeDetailResp extends BaseDetailResp { @ExcelProperty(value = "标题", order = 2) private String title; + /** + * 分类(取值于字典 notice_type) + */ + @Schema(description = "分类(取值于字典 notice_type)", example = "1") + @ExcelProperty(value = "分类", converter = ExcelDictConverter.class, order = 3) + @DictExcelProperty("notice_type") + private String type; + /** * 内容 */ @@ -59,47 +67,49 @@ public class NoticeDetailResp extends BaseDetailResp { private String content; /** - * 类型(取值于字典 notice_type) + * 通知范围 */ - @Schema(description = "类型(取值于字典 notice_type)", example = "1") - @ExcelProperty(value = "类型", converter = ExcelDictConverter.class, order = 3) - @DictExcelProperty("notice_type") - private String type; + @Schema(description = "通知范围", example = "2") + @ExcelProperty(value = "通知范围", converter = ExcelBaseEnumConverter.class, order = 4) + private NoticeScopeEnum noticeScope; + + /** + * 通知用户 + */ + @Schema(description = "通知用户", example = "[1,2,3]") + private List noticeUsers; + + /** + * 通知方式 + */ + @Schema(description = "通知方式", example = "[1,2]") + private List noticeMethods; + + /** + * 是否定时 + */ + @Schema(description = "是否定时", example = "false") + @ExcelProperty(value = "是否定时", order = 5) + private Boolean isTiming; + + /** + * 发布时间 + */ + @Schema(description = "发布时间", example = "2023-08-08 00:00:00", type = "string") + @ExcelProperty(value = "发布时间", order = 6) + private LocalDateTime publishTime; + + /** + * 是否置顶 + */ + @Schema(description = "是否置顶", example = "false") + @ExcelProperty(value = "是否置顶", order = 7) + private Boolean isTop; /** * 状态 */ - @Schema(description = "状态", example = "1") - @ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class, order = 4) + @Schema(description = "状态", example = "3") + @ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class, order = 8) private NoticeStatusEnum status; - - /** - * 生效时间 - */ - @Schema(description = "生效时间", example = "2023-08-08 00:00:00", type = "string") - @ExcelProperty(value = "生效时间", order = 5) - private LocalDateTime effectiveTime; - - /** - * 终止时间 - */ - @Schema(description = "终止时间", example = "2023-08-08 23:59:59", type = "string") - @ExcelProperty(value = "终止时间", order = 6) - private LocalDateTime terminateTime; - - /** - * 通知范围 - */ - @Schema(description = "通知范围", example = "2") - private NoticeScopeEnum noticeScope; - - /** - * 指定用户 - */ - @Schema(description = "指定用户", example = "[1,2,3]") - private List noticeUsers; - - public NoticeStatusEnum getStatus() { - return NoticeStatusEnum.getStatus(effectiveTime, terminateTime); - } } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/notice/NoticeResp.java similarity index 65% rename from continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeResp.java rename to continew-module-system/src/main/java/top/continew/admin/system/model/resp/notice/NoticeResp.java index cd32c8f1..eadf6a6f 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/NoticeResp.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/notice/NoticeResp.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package top.continew.admin.system.model.resp; +package top.continew.admin.system.model.resp.notice; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -46,33 +46,11 @@ public class NoticeResp extends BaseResp { private String title; /** - * 类型(取值于字典 notice_type) + * 分类(取值于字典 notice_type) */ - @Schema(description = "类型(取值于字典 notice_type)", example = "1") + @Schema(description = "分类(取值于字典 notice_type)", example = "1") private String type; - /** - * 生效时间 - */ - @Schema(description = "生效时间", example = "2023-08-08 00:00:00", type = "string") - private LocalDateTime effectiveTime; - - /** - * 终止时间 - */ - @Schema(description = "终止时间", example = "2023-08-08 23:59:59", type = "string") - private LocalDateTime terminateTime; - - /** - * 状态 - * - * @return 公告状态 - */ - @Schema(description = "状态", example = "1") - public NoticeStatusEnum getStatus() { - return NoticeStatusEnum.getStatus(effectiveTime, terminateTime); - } - /** * 通知范围 */ @@ -80,8 +58,38 @@ public class NoticeResp extends BaseResp { private NoticeScopeEnum noticeScope; /** - * 指定用户 + * 通知方式 */ - @Schema(description = "指定用户", example = "[1,2,3]") - private List noticeUsers; + @Schema(description = "通知方式", example = "[1,2]") + private List noticeMethods; + + /** + * 是否定时 + */ + @Schema(description = "是否定时", example = "false") + private Boolean isTiming; + + /** + * 发布时间 + */ + @Schema(description = "发布时间", example = "2023-08-08 00:00:00", type = "string") + private LocalDateTime publishTime; + + /** + * 是否置顶 + */ + @Schema(description = "是否置顶", example = "false") + private Boolean isTop; + + /** + * 状态 + */ + @Schema(description = "状态", example = "3") + private NoticeStatusEnum status; + + /** + * 是否已读 + */ + @Schema(description = "是否已读", example = "false") + private Boolean isRead; } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/notice/NoticeUnreadResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/notice/NoticeUnreadResp.java new file mode 100644 index 00000000..dc7f7891 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/notice/NoticeUnreadResp.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.model.resp.notice; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 未读公告响应参数 + * + * @author Charles7c + * @since 2025/5/20 22:00 + */ +@Data +@NoArgsConstructor +@Schema(description = "未读公告响应参数") +public class NoticeUnreadResp implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 未读公告数量 + */ + @Schema(description = "未读公告数量", example = "1") + private Long total; + + public NoticeUnreadResp(Long total) { + this.total = total; + } +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/MessageUserService.java b/continew-module-system/src/main/java/top/continew/admin/system/service/MessageLogService.java similarity index 59% rename from continew-module-system/src/main/java/top/continew/admin/system/service/MessageUserService.java rename to continew-module-system/src/main/java/top/continew/admin/system/service/MessageLogService.java index 6f515025..c464b924 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/MessageUserService.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/MessageLogService.java @@ -16,41 +16,32 @@ package top.continew.admin.system.service; -import top.continew.admin.system.model.resp.message.MessageUnreadResp; - import java.util.List; /** - * 消息和用户关联业务接口 + * 消息日志业务接口 * * @author Bull-BCLS + * @author Charles7c * @since 2023/10/15 19:05 */ -public interface MessageUserService { - - /** - * 根据用户 ID 查询未读消息数量 - * - * @param userId 用户 ID - * @param isDetail 是否查询详情 - * @return 未读消息信息 - */ - MessageUnreadResp countUnreadMessageByUserId(Long userId, Boolean isDetail); +public interface MessageLogService { /** * 新增 * - * @param messageId 消息 ID - * @param userIdList 用户 ID 列表 + * @param userIds 用户 ID 列表 + * @param messageId 消息 ID */ - void add(Long messageId, List userIdList); + void addWithMessageId(List userIds, Long messageId); /** - * 将消息标记已读 + * 新增 * - * @param ids 消息ID(为空则将所有消息标记已读) + * @param messageIds 消息 ID 列表 + * @param userId 用户 ID */ - void readMessage(List ids); + void addWithUserId(List messageIds, Long userId); /** * 根据消息 ID 删除 diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/MessageService.java b/continew-module-system/src/main/java/top/continew/admin/system/service/MessageService.java index 1fc27f45..f5c47c92 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/MessageService.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/MessageService.java @@ -19,6 +19,7 @@ package top.continew.admin.system.service; import top.continew.admin.system.model.query.MessageQuery; import top.continew.admin.system.model.req.MessageReq; import top.continew.admin.system.model.resp.message.MessageResp; +import top.continew.admin.system.model.resp.message.MessageUnreadResp; import top.continew.starter.extension.crud.model.query.PageQuery; import top.continew.starter.extension.crud.model.resp.PageResp; @@ -28,6 +29,7 @@ import java.util.List; * 消息业务接口 * * @author Bull-BCLS + * @author Charles7c * @since 2023/10/15 19:05 */ public interface MessageService { @@ -41,13 +43,30 @@ public interface MessageService { */ PageResp page(MessageQuery query, PageQuery pageQuery); + /** + * 将消息标记已读 + * + * @param ids 消息ID(为空则将所有消息标记已读) + * @param userId 用户ID + */ + void readMessage(List ids, Long userId); + + /** + * 查询未读消息数量 + * + * @param userId 用户 ID + * @param isDetail 是否查询详情 + * @return 未读消息数量 + */ + MessageUnreadResp countUnreadByUserId(Long userId, Boolean isDetail); + /** * 新增 * * @param req 请求参数 * @param userIdList 接收人列表 */ - void add(MessageReq req, List userIdList); + void add(MessageReq req, List userIdList); /** * 删除 diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/NoticeLogService.java b/continew-module-system/src/main/java/top/continew/admin/system/service/NoticeLogService.java new file mode 100644 index 00000000..425521e2 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/NoticeLogService.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.service; + +import java.util.List; + +/** + * 公告日志业务接口 + * + * @author Charles7c + * @since 2025/5/18 19:12 + */ +public interface NoticeLogService { + + /** + * 新增 + * + * @param userIds 用户 ID 列表 + * @param noticeId 公告 ID + * @return 是否新增成功(true:成功;false:无变更/失败) + */ + boolean add(List userIds, Long noticeId); + + /** + * 根据公告 ID 列表删除 + * + * @param noticeIds 公告 ID 列表 + */ + void deleteByNoticeIds(List noticeIds); + + /** + * 根据公告 ID 查询用户 ID 列表 + * + * @param noticeId 公告 ID + * @return 用户 ID 列表 + */ + List listUserIdByNoticeId(Long noticeId); +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/NoticeService.java b/continew-module-system/src/main/java/top/continew/admin/system/service/NoticeService.java index 795d9a2e..ecc6d6a3 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/NoticeService.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/NoticeService.java @@ -19,9 +19,10 @@ package top.continew.admin.system.service; import top.continew.admin.system.model.entity.NoticeDO; import top.continew.admin.system.model.query.NoticeQuery; import top.continew.admin.system.model.req.NoticeReq; -import top.continew.admin.system.model.resp.NoticeDetailResp; -import top.continew.admin.system.model.resp.NoticeResp; import top.continew.admin.system.model.resp.dashboard.DashboardNoticeResp; +import top.continew.admin.system.model.resp.notice.NoticeDetailResp; +import top.continew.admin.system.model.resp.notice.NoticeResp; +import top.continew.admin.system.model.resp.notice.NoticeUnreadResp; import top.continew.starter.data.mp.service.IService; import top.continew.starter.extension.crud.service.BaseService; @@ -35,6 +36,29 @@ import java.util.List; */ public interface NoticeService extends BaseService, IService { + /** + * 发布公告 + * + * @param notice 公告信息 + */ + void publish(NoticeDO notice); + + /** + * 查询未读公告数量 + * + * @param userId 用户 ID + * @return 未读公告响应参数 + */ + NoticeUnreadResp countUnreadByUserId(Long userId); + + /** + * 阅读公告 + * + * @param id 公告 ID + * @param userId 用户 ID + */ + void readNotice(Long id, Long userId); + /** * 查询仪表盘公告列表 * diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/MessageLogServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/MessageLogServiceImpl.java new file mode 100644 index 00000000..5ef39640 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/MessageLogServiceImpl.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import top.continew.admin.system.mapper.MessageLogMapper; +import top.continew.admin.system.model.entity.MessageLogDO; +import top.continew.admin.system.service.MessageLogService; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 消息日志业务实现 + * + * @author Bull-BCLS + * @author Charles7c + * @since 2023/10/15 19:05 + */ +@Service +@RequiredArgsConstructor +public class MessageLogServiceImpl implements MessageLogService { + + private final MessageLogMapper baseMapper; + + @Override + public void addWithMessageId(List userIdList, Long messageId) { + if (CollUtil.isEmpty(userIdList)) { + return; + } + List list = userIdList.stream() + .map(userId -> new MessageLogDO(userId, messageId, LocalDateTime.now())) + .toList(); + baseMapper.insert(list); + } + + @Override + public void addWithUserId(List messageIds, Long userId) { + if (CollUtil.isEmpty(messageIds)) { + return; + } + List list = messageIds.stream() + .map(messageId -> new MessageLogDO(userId, messageId, LocalDateTime.now())) + .toList(); + baseMapper.insert(list); + } + + @Override + public void deleteByMessageIds(List messageIds) { + if (CollUtil.isEmpty(messageIds)) { + return; + } + baseMapper.lambdaUpdate().in(MessageLogDO::getMessageId, messageIds).remove(); + } +} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/MessageServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/MessageServiceImpl.java index efb6e715..586b70d4 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/MessageServiceImpl.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/MessageServiceImpl.java @@ -17,32 +17,37 @@ package top.continew.admin.system.service.impl; import cn.crane4j.annotation.AutoOperate; +import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.system.enums.MessageTypeEnum; +import top.continew.admin.system.enums.NoticeScopeEnum; import top.continew.admin.system.mapper.MessageMapper; import top.continew.admin.system.model.entity.MessageDO; import top.continew.admin.system.model.query.MessageQuery; import top.continew.admin.system.model.req.MessageReq; import top.continew.admin.system.model.resp.message.MessageResp; +import top.continew.admin.system.model.resp.message.MessageTypeUnreadResp; +import top.continew.admin.system.model.resp.message.MessageUnreadResp; +import top.continew.admin.system.service.MessageLogService; import top.continew.admin.system.service.MessageService; -import top.continew.admin.system.service.MessageUserService; -import top.continew.starter.core.validation.CheckUtils; -import top.continew.starter.data.mp.util.QueryWrapperHelper; import top.continew.starter.extension.crud.model.query.PageQuery; import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.messaging.websocket.util.WebSocketUtils; +import java.util.ArrayList; import java.util.List; /** * 消息业务实现 * * @author Bull-BCLS + * @author Charles7c * @since 2023/10/15 19:05 */ @Service @@ -50,32 +55,71 @@ import java.util.List; public class MessageServiceImpl implements MessageService { private final MessageMapper baseMapper; - private final MessageUserService messageUserService; + private final MessageLogService messageLogService; @Override @AutoOperate(type = MessageResp.class, on = "list") public PageResp page(MessageQuery query, PageQuery pageQuery) { - QueryWrapper queryWrapper = QueryWrapperHelper.build(query, pageQuery.getSort()); - queryWrapper.apply(null != query.getUserId(), "t2.user_id={0}", query.getUserId()) - .apply(null != query.getIsRead(), "t2.is_read={0}", query.getIsRead()); - IPage page = baseMapper.selectPageByUserId(new Page<>(pageQuery.getPage(), pageQuery - .getSize()), queryWrapper); + IPage page = baseMapper.selectMessagePage(new Page<>(pageQuery.getPage(), pageQuery + .getSize()), query); return PageResp.build(page); } + @Override + public void readMessage(List ids, Long userId) { + if (CollUtil.isEmpty(ids)) { + // 查询当前用户的未读消息 + List list = baseMapper.selectUnreadListByUserId(userId); + ids = list.stream().map(MessageDO::getId).toList(); + } + messageLogService.addWithMessageId(ids, userId); + } + + @Override + public MessageUnreadResp countUnreadByUserId(Long userId, Boolean isDetail) { + MessageUnreadResp result = new MessageUnreadResp(); + Long total = 0L; + if (Boolean.TRUE.equals(isDetail)) { + List detailList = new ArrayList<>(); + for (MessageTypeEnum messageType : MessageTypeEnum.values()) { + MessageTypeUnreadResp resp = new MessageTypeUnreadResp(); + resp.setType(messageType); + Long count = baseMapper.selectUnreadCountByUserIdAndType(userId, messageType.getValue()); + resp.setCount(count); + detailList.add(resp); + total += count; + } + result.setDetails(detailList); + } else { + total = baseMapper.selectUnreadCountByUserIdAndType(userId, null); + } + result.setTotal(total); + return result; + } + @Override @Transactional(rollbackFor = Exception.class) - public void add(MessageReq req, List userIdList) { - CheckUtils.throwIf(() -> CollUtil.isEmpty(userIdList), "消息接收人不能为空"); + public void add(MessageReq req, List userIdList) { MessageDO message = BeanUtil.copyProperties(req, MessageDO.class); + message.setScope(CollUtil.isEmpty(userIdList) ? NoticeScopeEnum.ALL : NoticeScopeEnum.USER); + message.setUsers(userIdList); baseMapper.insert(message); - messageUserService.add(message.getId(), userIdList); + // 发送消息给指定在线用户 + if (CollUtil.isNotEmpty(userIdList)) { + userIdList.parallelStream().forEach(userId -> { + List tokenList = StpUtil.getTokenValueListByLoginId(userId); + tokenList.parallelStream().forEach(token -> WebSocketUtils.sendMessage(token, "1")); + }); + return; + } + // 发送消息给所有在线用户 + // TODO WebSocketUtils.sendMessage("1"); } @Override @Transactional(rollbackFor = Exception.class) public void delete(List ids) { baseMapper.deleteByIds(ids); - messageUserService.deleteByMessageIds(ids); + messageLogService.deleteByMessageIds(ids); } } \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/MessageUserServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/MessageUserServiceImpl.java deleted file mode 100644 index 92d7c5c2..00000000 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/MessageUserServiceImpl.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package top.continew.admin.system.service.impl; - -import cn.hutool.core.collection.CollUtil; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import top.continew.admin.system.enums.MessageTypeEnum; -import top.continew.admin.system.mapper.MessageUserMapper; -import top.continew.admin.system.model.entity.MessageUserDO; -import top.continew.admin.system.model.resp.message.MessageTypeUnreadResp; -import top.continew.admin.system.model.resp.message.MessageUnreadResp; -import top.continew.admin.system.service.MessageUserService; -import top.continew.starter.core.validation.CheckUtils; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; - -/** - * 消息和用户关联业务实现 - * - * @author Bull-BCLS - * @since 2023/10/15 19:05 - */ -@Service -@RequiredArgsConstructor -public class MessageUserServiceImpl implements MessageUserService { - - private final MessageUserMapper baseMapper; - - @Override - public MessageUnreadResp countUnreadMessageByUserId(Long userId, Boolean isDetail) { - MessageUnreadResp result = new MessageUnreadResp(); - Long total = 0L; - if (Boolean.TRUE.equals(isDetail)) { - List detailList = new ArrayList<>(); - for (MessageTypeEnum messageType : MessageTypeEnum.values()) { - MessageTypeUnreadResp resp = new MessageTypeUnreadResp(); - resp.setType(messageType); - Long count = baseMapper.selectUnreadCountByUserIdAndType(userId, messageType.getValue()); - resp.setCount(count); - detailList.add(resp); - total += count; - } - result.setDetails(detailList); - } else { - total = baseMapper.selectUnreadCountByUserIdAndType(userId, null); - } - result.setTotal(total); - return result; - } - - @Override - public void add(Long messageId, List userIdList) { - CheckUtils.throwIfEmpty(userIdList, "消息接收人不能为空"); - List messageUserList = userIdList.stream().map(userId -> { - MessageUserDO messageUser = new MessageUserDO(); - messageUser.setUserId(userId); - messageUser.setMessageId(messageId); - messageUser.setIsRead(false); - return messageUser; - }).toList(); - baseMapper.insert(messageUserList); - } - - @Override - public void readMessage(List ids) { - baseMapper.lambdaUpdate() - .set(MessageUserDO::getIsRead, true) - .set(MessageUserDO::getReadTime, LocalDateTime.now()) - .eq(MessageUserDO::getIsRead, false) - .in(CollUtil.isNotEmpty(ids), MessageUserDO::getMessageId, ids) - .update(); - } - - @Override - public void deleteByMessageIds(List messageIds) { - if (CollUtil.isEmpty(messageIds)) { - return; - } - baseMapper.lambdaUpdate().in(MessageUserDO::getMessageId, messageIds).remove(); - } -} \ No newline at end of file diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/NoticeLogServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/NoticeLogServiceImpl.java new file mode 100644 index 00000000..70409a61 --- /dev/null +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/NoticeLogServiceImpl.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.system.mapper.NoticeLogMapper; +import top.continew.admin.system.model.entity.NoticeLogDO; +import top.continew.admin.system.service.NoticeLogService; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; + +/** + * 公告日志业务实现 + * + * @author Charles7c + * @since 2025/5/18 19:15 + */ +@Service +@RequiredArgsConstructor +public class NoticeLogServiceImpl implements NoticeLogService { + + private final NoticeLogMapper baseMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean add(List userIds, Long noticeId) { + // 检查是否有变更 + List oldUserIdList = baseMapper.lambdaQuery() + .select(NoticeLogDO::getUserId) + .eq(NoticeLogDO::getNoticeId, noticeId) + .list() + .stream() + .map(NoticeLogDO::getUserId) + .toList(); + Collection subtract = CollUtil.subtract(userIds, oldUserIdList); + if (CollUtil.isEmpty(subtract)) { + return false; + } + // 新增没有关联的 + LocalDateTime now = LocalDateTime.now(); + List list = subtract.stream().map(userId -> new NoticeLogDO(noticeId, userId, now)).toList(); + return baseMapper.insertBatch(list); + } + + @Override + public void deleteByNoticeIds(List noticeIds) { + if (CollUtil.isEmpty(noticeIds)) { + return; + } + baseMapper.lambdaUpdate().in(NoticeLogDO::getNoticeId, noticeIds).remove(); + } + + @Override + public List listUserIdByNoticeId(Long noticeId) { + return baseMapper.lambdaQuery() + .select(NoticeLogDO::getUserId) + .eq(NoticeLogDO::getNoticeId, noticeId) + .list() + .stream() + .map(NoticeLogDO::getUserId) + .toList(); + } +} diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/NoticeServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/NoticeServiceImpl.java index 04475f6b..811438e6 100644 --- a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/NoticeServiceImpl.java +++ b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/NoticeServiceImpl.java @@ -16,23 +16,32 @@ package top.continew.admin.system.service.impl; +import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import top.continew.admin.common.context.UserContextHolder; +import top.continew.admin.system.enums.*; import top.continew.admin.system.mapper.NoticeMapper; import top.continew.admin.system.model.entity.NoticeDO; import top.continew.admin.system.model.query.NoticeQuery; +import top.continew.admin.system.model.req.MessageReq; import top.continew.admin.system.model.req.NoticeReq; -import top.continew.admin.system.model.resp.NoticeDetailResp; -import top.continew.admin.system.model.resp.NoticeResp; import top.continew.admin.system.model.resp.dashboard.DashboardNoticeResp; +import top.continew.admin.system.model.resp.notice.NoticeDetailResp; +import top.continew.admin.system.model.resp.notice.NoticeResp; +import top.continew.admin.system.model.resp.notice.NoticeUnreadResp; +import top.continew.admin.system.service.MessageService; +import top.continew.admin.system.service.NoticeLogService; import top.continew.admin.system.service.NoticeService; +import top.continew.starter.core.validation.CheckUtils; +import top.continew.starter.core.validation.ValidationUtils; import top.continew.starter.extension.crud.model.query.PageQuery; import top.continew.starter.extension.crud.model.resp.PageResp; import top.continew.starter.extension.crud.service.BaseServiceImpl; +import java.time.LocalDateTime; import java.util.List; /** @@ -45,15 +54,130 @@ import java.util.List; @RequiredArgsConstructor public class NoticeServiceImpl extends BaseServiceImpl implements NoticeService { + private final NoticeLogService noticeLogService; + private final MessageService messageService; + @Override public PageResp page(NoticeQuery query, PageQuery pageQuery) { - IPage page = baseMapper.selectNoticePage(new Page<>(pageQuery.getPage(), pageQuery + IPage page = baseMapper.selectNoticePage(new Page<>(pageQuery.getPage(), pageQuery .getSize()), query); - PageResp pageResp = PageResp.build(page, super.getListClass()); + PageResp pageResp = PageResp.build(page); pageResp.getList().forEach(this::fill); return pageResp; } + @Override + public void beforeCreate(NoticeReq req) { + // 校验定时发布 + if (Boolean.TRUE.equals(req.getIsTiming())) { + ValidationUtils.throwIf(req.getPublishTime() == null, "定时发布时间不能为空"); + ValidationUtils.throwIf(req.getPublishTime().isBefore(LocalDateTime.now()), "定时发布时间不能早于当前时间"); + } + if (!NoticeStatusEnum.DRAFT.equals(req.getStatus())) { + if (Boolean.TRUE.equals(req.getIsTiming())) { + // 待发布 + req.setStatus(NoticeStatusEnum.PENDING); + } else { + // 已发布 + req.setStatus(NoticeStatusEnum.PUBLISHED); + req.setPublishTime(LocalDateTime.now()); + } + } + } + + @Override + public void afterCreate(NoticeReq req, NoticeDO entity) { + // 发送消息 + if (NoticeStatusEnum.PUBLISHED.equals(entity.getStatus())) { + this.publish(entity); + } + } + + @Override + public void beforeUpdate(NoticeReq req, Long id) { + NoticeDO oldNotice = super.getById(id); + switch (oldNotice.getStatus()) { + case PUBLISHED -> { + CheckUtils.throwIfNotEqual(req.getStatus(), oldNotice.getStatus(), "公告已发布,不允许修改状态"); + CheckUtils.throwIfNotEqual(req.getIsTiming(), oldNotice.getIsTiming(), "公告已发布,不允许修改定时发布信息"); + CheckUtils.throwIfNotEqual(req.getNoticeScope(), oldNotice.getNoticeScope(), "公告已发布,不允许修改通知范围"); + if (NoticeScopeEnum.USER.equals(oldNotice.getNoticeScope())) { + CheckUtils.throwIfNotEmpty(CollUtil.disjunction(req.getNoticeUsers(), oldNotice + .getNoticeUsers()), "公告已发布,不允许修改通知用户"); + } + CheckUtils.throwIf(!CollUtil.isEqualList(req.getNoticeMethods(), oldNotice + .getNoticeMethods()), "公告已发布,不允许修改通知方式"); + // 修正定时发布信息 + if (Boolean.TRUE.equals(oldNotice.getIsTiming())) { + CheckUtils.throwIfNotEqual(req.getPublishTime(), oldNotice.getPublishTime(), "公告已发布,不允许修改定时发布信息"); + } + req.setPublishTime(oldNotice.getPublishTime()); + } + case DRAFT, PENDING -> { + // 校验定时发布 + if (Boolean.TRUE.equals(req.getIsTiming())) { + ValidationUtils.throwIf(req.getPublishTime() == null, "定时发布时间不能为空"); + ValidationUtils.throwIf(req.getPublishTime().isBefore(LocalDateTime.now()), "定时发布时间不能早于当前时间"); + } + // 已发布 + if (NoticeStatusEnum.PUBLISHED.equals(req.getStatus())) { + if (Boolean.TRUE.equals(req.getIsTiming())) { + // 待发布 + req.setStatus(NoticeStatusEnum.PENDING); + } else { + // 已发布 + req.setStatus(NoticeStatusEnum.PUBLISHED); + req.setPublishTime(LocalDateTime.now()); + } + } + } + default -> throw new IllegalArgumentException("状态无效"); + } + } + + @Override + public void afterUpdate(NoticeReq req, NoticeDO entity) { + // 重置定时发布时间 + if (!NoticeStatusEnum.PUBLISHED.equals(entity.getStatus()) && Boolean.FALSE.equals(entity + .getIsTiming()) && entity.getPublishTime() != null) { + baseMapper.lambdaUpdate().set(NoticeDO::getPublishTime, null).eq(NoticeDO::getId, entity.getId()).update(); + } + // 发送消息 + if (Boolean.FALSE.equals(entity.getIsTiming()) && NoticeStatusEnum.PUBLISHED.equals(entity.getStatus())) { + this.publish(entity); + } + } + + @Override + public void afterDelete(List ids) { + // 删除公告日志 + noticeLogService.deleteByNoticeIds(ids); + } + + @Override + public void publish(NoticeDO notice) { + List noticeMethods = notice.getNoticeMethods(); + if (CollUtil.isNotEmpty(noticeMethods) && noticeMethods.contains(NoticeMethodEnum.SYSTEM_MESSAGE.getValue())) { + MessageTemplateEnum template = MessageTemplateEnum.NOTICE_PUBLISH; + MessageReq req = new MessageReq(MessageTypeEnum.SYSTEM); + req.setTitle(template.getTitle()); + req.setContent(template.getContent().formatted(notice.getTitle())); + req.setPath(template.getPath().formatted(notice.getId())); + // 新增消息 + messageService.add(req, notice.getNoticeUsers()); + } + } + + @Override + public NoticeUnreadResp countUnreadByUserId(Long userId) { + return new NoticeUnreadResp(baseMapper.selectUnreadCountByUserId(userId)); + } + + @Override + public void readNotice(Long id, Long userId) { + noticeLogService.add(List.of(userId), id); + } + @Override public List listDashboard() { Long userId = UserContextHolder.getUserId(); diff --git a/continew-module-system/src/main/resources/mapper/MessageLogMapper.xml b/continew-module-system/src/main/resources/mapper/MessageLogMapper.xml new file mode 100644 index 00000000..597f3f9f --- /dev/null +++ b/continew-module-system/src/main/resources/mapper/MessageLogMapper.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/continew-module-system/src/main/resources/mapper/MessageMapper.xml b/continew-module-system/src/main/resources/mapper/MessageMapper.xml index 7cc15625..bfe55736 100644 --- a/continew-module-system/src/main/resources/mapper/MessageMapper.xml +++ b/continew-module-system/src/main/resources/mapper/MessageMapper.xml @@ -1,14 +1,56 @@ - SELECT - t1.*, - t2.user_id, - t2.is_read, - t2.read_time + t1.id, + t1.title, + t1.content, + t1.type, + t1.path, + t1.scope, + t1.users, + t1.create_time, + t2.read_time IS NOT NULL AS isRead, + t2.read_time AS readTime FROM sys_message AS t1 - LEFT JOIN sys_message_user AS t2 ON t2.message_id = t1.id - ${ew.getCustomSqlSegment} + LEFT JOIN sys_message_log AS t2 ON t2.message_id = t1.id + + + (t1.scope = 1 OR (t1.scope = 2 AND JSON_EXTRACT(t1.users, "$[0]") = CAST(#{query.userId} AS CHAR))) + + + AND t1.title LIKE CONCAT('%', #{query.title}, '%') + + + AND t1.type = #{query.type} + + + AND t2.read_time IS NOT NULL + + + ORDER BY t1.create_time DESC + + + + + diff --git a/continew-module-system/src/main/resources/mapper/MessageUserMapper.xml b/continew-module-system/src/main/resources/mapper/MessageUserMapper.xml deleted file mode 100644 index 8b6ae76e..00000000 --- a/continew-module-system/src/main/resources/mapper/MessageUserMapper.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - \ No newline at end of file diff --git a/continew-module-system/src/main/resources/mapper/NoticeMapper.xml b/continew-module-system/src/main/resources/mapper/NoticeMapper.xml index 34f091ea..8372b890 100644 --- a/continew-module-system/src/main/resources/mapper/NoticeMapper.xml +++ b/continew-module-system/src/main/resources/mapper/NoticeMapper.xml @@ -2,28 +2,29 @@ - + + + + - + SELECT + t1.id, + t1.title, + t1.type, + t1.notice_scope, + t1.notice_methods, + t1.is_timing, + t1.publish_time, + t1.is_top, + t1.status, + t1.create_user, + t2.read_time IS NOT NULL AS isRead FROM sys_notice AS t1 + LEFT JOIN sys_notice_log AS t2 ON t2.notice_id = t1.id - t1.notice_scope = 1 OR (t1.notice_scope = 2 AND JSON_EXTRACT(t1.notice_users, "$[0]") = CAST(#{query.userId} AS CHAR)) - AND (t1.effective_time IS NULL OR NOW() > t1.effective_time) - AND (t1.terminate_time IS NULL OR t1.terminate_time > NOW()) + (t1.notice_scope = 1 OR (t1.notice_scope = 2 AND JSON_EXTRACT(t1.notice_users, "$[0]") = CAST(#{query.userId} AS CHAR))) AND t1.title LIKE CONCAT('%', #{query.title}, '%') @@ -33,10 +34,32 @@ - ORDER BY t1.sort ASC, t1.effective_time DESC + ORDER BY t1.is_top DESC, t1.publish_time DESC ORDER BY t1.create_time DESC + + + + \ No newline at end of file diff --git a/continew-webapi/src/main/java/top/continew/admin/controller/system/MessageController.java b/continew-webapi/src/main/java/top/continew/admin/controller/system/MessageController.java deleted file mode 100644 index 8a23c406..00000000 --- a/continew-webapi/src/main/java/top/continew/admin/controller/system/MessageController.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package top.continew.admin.controller.system; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.enums.ParameterIn; -import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.RequiredArgsConstructor; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; -import top.continew.admin.common.context.UserContextHolder; -import top.continew.admin.system.model.query.MessageQuery; -import top.continew.admin.system.model.resp.message.MessageResp; -import top.continew.admin.system.model.resp.message.MessageUnreadResp; -import top.continew.admin.system.service.MessageService; -import top.continew.admin.system.service.MessageUserService; -import top.continew.starter.extension.crud.model.query.PageQuery; -import top.continew.starter.extension.crud.model.req.IdsReq; -import top.continew.starter.extension.crud.model.resp.PageResp; -import top.continew.starter.log.annotation.Log; - -/** - * 消息管理 API - * - * @author Bull-BCLS - * @since 2023/10/15 19:05 - */ -@Tag(name = "消息管理 API") -@RestController -@RequiredArgsConstructor -@RequestMapping("/system/message") -public class MessageController { - - private final MessageService baseService; - private final MessageUserService messageUserService; - - @Operation(summary = "分页查询列表", description = "分页查询列表") - @GetMapping - public PageResp page(MessageQuery query, @Validated PageQuery pageQuery) { - query.setUserId(UserContextHolder.getUserId()); - return baseService.page(query, pageQuery); - } - - @Operation(summary = "删除数据", description = "删除数据") - @DeleteMapping - public void delete(@Validated @RequestBody IdsReq req) { - baseService.delete(req.getIds()); - } - - @Operation(summary = "标记已读", description = "将消息标记为已读状态") - @PatchMapping("/read") - public void read(@Validated @RequestBody IdsReq req) { - messageUserService.readMessage(req.getIds()); - } - - @Operation(summary = "全部已读", description = "将所有消息标记为已读状态") - @PatchMapping("/readAll") - public void readAll() { - messageUserService.readMessage(null); - } - - @Log(ignore = true) - @Operation(summary = "查询未读消息数量", description = "查询当前用户的未读消息数量") - @Parameter(name = "isDetail", description = "是否查询详情", example = "true", in = ParameterIn.QUERY) - @GetMapping("/unread") - public MessageUnreadResp countUnread(@RequestParam(required = false) Boolean detail) { - return messageUserService.countUnreadMessageByUserId(UserContextHolder.getUserId(), detail); - } -} \ No newline at end of file diff --git a/continew-webapi/src/main/java/top/continew/admin/controller/system/NoticeController.java b/continew-webapi/src/main/java/top/continew/admin/controller/system/NoticeController.java index ac52a9ad..b1dd718c 100644 --- a/continew-webapi/src/main/java/top/continew/admin/controller/system/NoticeController.java +++ b/continew-webapi/src/main/java/top/continew/admin/controller/system/NoticeController.java @@ -16,14 +16,16 @@ package top.continew.admin.controller.system; +import cn.hutool.core.collection.CollUtil; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.RestController; import top.continew.admin.common.controller.BaseController; +import top.continew.admin.system.enums.NoticeMethodEnum; import top.continew.admin.system.enums.NoticeScopeEnum; import top.continew.admin.system.model.query.NoticeQuery; import top.continew.admin.system.model.req.NoticeReq; -import top.continew.admin.system.model.resp.NoticeDetailResp; -import top.continew.admin.system.model.resp.NoticeResp; +import top.continew.admin.system.model.resp.notice.NoticeDetailResp; +import top.continew.admin.system.model.resp.notice.NoticeResp; import top.continew.admin.system.service.NoticeService; import top.continew.starter.core.validation.ValidationUtils; import top.continew.starter.extension.crud.annotation.CrudApi; @@ -31,7 +33,8 @@ import top.continew.starter.extension.crud.annotation.CrudRequestMapping; import top.continew.starter.extension.crud.enums.Api; import java.lang.reflect.Method; -import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.List; /** * 公告管理 API @@ -52,15 +55,18 @@ public class NoticeController extends BaseController noticeMethods = req.getNoticeMethods(); + if (CollUtil.isNotEmpty(noticeMethods)) { + List validMethods = Arrays.stream(NoticeMethodEnum.values()) + .map(NoticeMethodEnum::getValue) + .toList(); + noticeMethods.forEach(method -> ValidationUtils.throwIf(!validMethods + .contains(method), "通知方式 [{}] 不正确", method)); + } } } \ No newline at end of file diff --git a/continew-webapi/src/main/java/top/continew/admin/controller/system/UserMessageController.java b/continew-webapi/src/main/java/top/continew/admin/controller/system/UserMessageController.java index f56a320f..6c7fba50 100644 --- a/continew-webapi/src/main/java/top/continew/admin/controller/system/UserMessageController.java +++ b/continew-webapi/src/main/java/top/continew/admin/controller/system/UserMessageController.java @@ -22,19 +22,24 @@ import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import top.continew.admin.common.context.UserContextHolder; import top.continew.admin.system.enums.NoticeScopeEnum; +import top.continew.admin.system.model.query.MessageQuery; import top.continew.admin.system.model.query.NoticeQuery; -import top.continew.admin.system.model.resp.NoticeDetailResp; -import top.continew.admin.system.model.resp.NoticeResp; +import top.continew.admin.system.model.resp.message.MessageResp; +import top.continew.admin.system.model.resp.message.MessageUnreadResp; +import top.continew.admin.system.model.resp.notice.NoticeDetailResp; +import top.continew.admin.system.model.resp.notice.NoticeResp; +import top.continew.admin.system.model.resp.notice.NoticeUnreadResp; +import top.continew.admin.system.service.MessageService; import top.continew.admin.system.service.NoticeService; import top.continew.starter.core.validation.CheckUtils; import top.continew.starter.extension.crud.model.query.PageQuery; +import top.continew.starter.extension.crud.model.req.IdsReq; import top.continew.starter.extension.crud.model.resp.BasePageResp; +import top.continew.starter.extension.crud.model.resp.PageResp; +import top.continew.starter.log.annotation.Log; /** * 个人消息 API @@ -50,6 +55,40 @@ import top.continew.starter.extension.crud.model.resp.BasePageResp; public class UserMessageController { private final NoticeService noticeService; + private final MessageService messageService; + + @Operation(summary = "分页查询消息列表", description = "分页查询消息列表") + @GetMapping + public PageResp page(MessageQuery query, @Validated PageQuery pageQuery) { + query.setUserId(UserContextHolder.getUserId()); + return messageService.page(query, pageQuery); + } + + @Operation(summary = "删除消息", description = "删除消息") + @DeleteMapping + public void delete(@Validated @RequestBody IdsReq req) { + messageService.delete(req.getIds()); + } + + @Operation(summary = "消息标记为已读", description = "将消息标记为已读状态") + @PatchMapping("/read") + public void read(@Validated @RequestBody IdsReq req) { + messageService.readMessage(req.getIds(), UserContextHolder.getUserId()); + } + + @Operation(summary = "消息全部已读", description = "将所有消息标记为已读状态") + @PatchMapping("/readAll") + public void readAll() { + messageService.readMessage(null, UserContextHolder.getUserId()); + } + + @Log(ignore = true) + @Operation(summary = "查询未读消息数量", description = "查询当前用户的未读消息数量") + @Parameter(name = "isDetail", description = "是否查询详情", example = "true", in = ParameterIn.QUERY) + @GetMapping("/unread") + public MessageUnreadResp countUnreadMessage(@RequestParam(required = false) Boolean detail) { + return messageService.countUnreadByUserId(UserContextHolder.getUserId(), detail); + } @Operation(summary = "分页查询公告列表", description = "分页查询公告列表") @GetMapping("/notice") @@ -66,6 +105,14 @@ public class UserMessageController { CheckUtils.throwIf(detail == null || (NoticeScopeEnum.USER.equals(detail.getNoticeScope()) && !detail .getNoticeUsers() .contains(UserContextHolder.getUserId().toString())), "公告不存在或无权限访问"); + noticeService.readNotice(id, UserContextHolder.getUserId()); return detail; } + + @Log(ignore = true) + @Operation(summary = "查询未读公告数量", description = "查询当前用户的未读公告数量") + @GetMapping("/notice/unread") + public NoticeUnreadResp countUnreadNotice() { + return noticeService.countUnreadByUserId(UserContextHolder.getUserId()); + } } diff --git a/continew-webapi/src/main/java/top/continew/admin/controller/schedule/DemoEnvironmentJob.java b/continew-webapi/src/main/java/top/continew/admin/job/DemoEnvironmentJob.java similarity index 96% rename from continew-webapi/src/main/java/top/continew/admin/controller/schedule/DemoEnvironmentJob.java rename to continew-webapi/src/main/java/top/continew/admin/job/DemoEnvironmentJob.java index 1706268d..2e10ac8a 100644 --- a/continew-webapi/src/main/java/top/continew/admin/controller/schedule/DemoEnvironmentJob.java +++ b/continew-webapi/src/main/java/top/continew/admin/job/DemoEnvironmentJob.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package top.continew.admin.controller.schedule; +package top.continew.admin.job; import cn.hutool.core.util.StrUtil; import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; @@ -53,8 +53,9 @@ public class DemoEnvironmentJob { private final DictMapper dictMapper; private final StorageMapper storageMapper; private final NoticeMapper noticeMapper; + private final NoticeLogMapper noticeLogMapper; private final MessageMapper messageMapper; - private final MessageUserMapper messageUserMapper; + private final MessageLogMapper messageLogMapper; private final UserMapper userMapper; private final UserRoleMapper userRoleMapper; private final UserSocialMapper userSocialMapper; @@ -109,7 +110,8 @@ public class DemoEnvironmentJob { InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().blockAttack(true).build()); SnailJobLog.REMOTE.info("演示环境待清理数据项检测完成,开始执行清理。"); // 清理关联数据 - messageUserMapper.lambdaUpdate().gt(MessageUserDO::getMessageId, MESSAGE_FLAG).remove(); + noticeLogMapper.lambdaUpdate().gt(NoticeLogDO::getNoticeId, DELETE_FLAG).remove(); + messageLogMapper.lambdaUpdate().gt(MessageLogDO::getMessageId, MESSAGE_FLAG).remove(); userRoleMapper.lambdaUpdate().notIn(UserRoleDO::getRoleId, ROLE_FLAG).remove(); userRoleMapper.lambdaUpdate().notIn(UserRoleDO::getUserId, USER_FLAG).remove(); roleDeptMapper.lambdaUpdate().notIn(RoleDeptDO::getRoleId, ROLE_FLAG).remove(); diff --git a/continew-webapi/src/main/java/top/continew/admin/job/NoticePublishJob.java b/continew-webapi/src/main/java/top/continew/admin/job/NoticePublishJob.java new file mode 100644 index 00000000..a1b2bbd1 --- /dev/null +++ b/continew-webapi/src/main/java/top/continew/admin/job/NoticePublishJob.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package top.continew.admin.job; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; +import com.aizuda.snailjob.common.log.SnailJobLog; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import top.continew.admin.schedule.annotation.ConditionalOnEnabledScheduleJob; +import top.continew.admin.system.enums.NoticeMethodEnum; +import top.continew.admin.system.enums.NoticeStatusEnum; +import top.continew.admin.system.mapper.NoticeMapper; +import top.continew.admin.system.model.entity.NoticeDO; +import top.continew.admin.system.service.NoticeService; +import top.continew.starter.core.constant.PropertiesConstants; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * 公告发布任务 + * + * @author Charles7c + * @since 2025/5/11 22:19 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class NoticePublishJob { + + /** + * 定时发布公告(未启用 Snail Job 则使用它) + */ + @Component + @ConditionalOnProperty(prefix = "snail-job", name = PropertiesConstants.ENABLED, havingValue = "false") + public static class Scheduler { + + @Scheduled(cron = "0 * * * * ?") + @Transactional(rollbackFor = Exception.class) + public void publishNoticeWithSchedule() { + log.info("定时任务 [公告发布] 开始执行。"); + publishNotice(); + log.info("定时任务 [公告发布] 执行结束。"); + } + } + + /** + * 定时发布公告(启用 Snail Job 时) + */ + @Component + @ConditionalOnEnabledScheduleJob + public static class ScheduleJob { + + @JobExecutor(name = "NoticePublishJob") + @Transactional(rollbackFor = Exception.class) + public void publishNoticeWithScheduleJob() { + SnailJobLog.REMOTE.info("定时任务 [公告发布] 开始执行。"); + publishNotice(); + SnailJobLog.REMOTE.info("定时任务 [公告发布] 执行结束。"); + } + } + + /** + * 发布公告 + */ + private static void publishNotice() { + NoticeMapper noticeMapper = SpringUtil.getBean(NoticeMapper.class); + // 查询待发布公告 + List list = noticeMapper.lambdaQuery() + .eq(NoticeDO::getStatus, NoticeStatusEnum.PENDING) + .le(NoticeDO::getPublishTime, LocalDateTime.now()) + .list(); + if (CollUtil.isEmpty(list)) { + return; + } + // 筛选需要发送消息的公告并发送 + List needSendMessageList = list.stream() + .filter(notice -> CollUtil.isNotEmpty(notice.getNoticeMethods())) + .filter(notice -> notice.getNoticeMethods().contains(NoticeMethodEnum.SYSTEM_MESSAGE.getValue())) + .toList(); + if (CollUtil.isNotEmpty(needSendMessageList)) { + // 发送消息 + NoticeService noticeService = SpringUtil.getBean(NoticeService.class); + needSendMessageList.parallelStream().forEach(noticeService::publish); + } + // 更新状态 + noticeMapper.lambdaUpdate() + .set(NoticeDO::getStatus, NoticeStatusEnum.PUBLISHED) + .in(NoticeDO::getId, list.stream().map(NoticeDO::getId).toList()) + .update(); + } +} diff --git a/continew-webapi/src/main/resources/db/changelog/mysql/main_data.sql b/continew-webapi/src/main/resources/db/changelog/mysql/main_data.sql index a5c1905d..524148c9 100644 --- a/continew-webapi/src/main/resources/db/changelog/mysql/main_data.sql +++ b/continew-webapi/src/main/resources/db/changelog/mysql/main_data.sql @@ -46,10 +46,11 @@ VALUES (1090, '通知公告', 1000, 2, '/system/notice', 'SystemNotice', 'system/notice/index', NULL, 'notification', b'0', b'0', b'0', NULL, 5, 1, 1, NOW()), (1091, '列表', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:list', 1, 1, 1, NOW()), -(1092, '公告详情', 1090, 2, '/system/notice/detail', 'SystemNoticeDetail', 'system/notice/detail/index', NULL, NULL, b'0', b'0', b'1', 'system:notice:get', 2, 1, 1, NOW()), -(1093, '发布公告', 1090, 2, '/system/notice/add', 'SystemNoticeAdd', 'system/notice/add/index', NULL, NULL, b'0', b'0', b'1', 'system:notice:create', 3, 1, 1, NOW()), -(1094, '修改', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:update', 4, 1, 1, NOW()), -(1095, '删除', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:delete', 5, 1, 1, NOW()), +(1092, '详情', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:get', 2, 1, 1, NOW()), +(1093, '查看公告', 1090, 2, '/system/notice/view', 'SystemNoticeView', 'system/notice/view/index', NULL, NULL, b'0', b'0', b'1', 'system:notice:view', 3, 1, 1, NOW()), +(1094, '发布公告', 1090, 2, '/system/notice/add', 'SystemNoticeAdd', 'system/notice/add/index', NULL, NULL, b'0', b'0', b'1', 'system:notice:create', 4, 1, 1, NOW()), +(1095, '修改', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:update', 5, 1, 1, NOW()), +(1096, '删除', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:delete', 6, 1, 1, NOW()), (1110, '文件管理', 1000, 2, '/system/file', 'SystemFile', 'system/file/index', NULL, 'file', b'0', b'0', b'0', NULL, 6, 1, 1, NOW()), (1111, '列表', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:list', 1, 1, 1, NOW()), @@ -215,24 +216,21 @@ VALUES INSERT INTO `sys_dict` (`id`, `name`, `code`, `description`, `is_system`, `create_user`, `create_time`) VALUES -(1, '公告类型', 'notice_type', NULL, b'1', 1, NOW()), -(2, '消息类型', 'message_type', NULL, b'1', 1, NOW()), -(3, '客户端类型', 'client_type', NULL, b'1', 1, NOW()), -(4, '短信厂商', 'sms_supplier', NULL, b'1', 1, NOW()); +(1, '公告分类', 'notice_type', NULL, b'1', 1, NOW()), +(2, '客户端类型', 'client_type', NULL, b'1', 1, NOW()), +(3, '短信厂商', 'sms_supplier', NULL, b'1', 1, NOW()); INSERT INTO `sys_dict_item` (`id`, `label`, `value`, `color`, `sort`, `description`, `status`, `dict_id`, `create_user`, `create_time`) VALUES -(1, '通知', '1', 'primary', 1, NULL, 1, 1, 1, NOW()), -(2, '活动', '2', 'success', 2, NULL, 1, 1, 1, NOW()), -(3, '安全消息', '1', 'warning', 1, NULL, 1, 2, 1, NOW()), -(4, '活动消息', '2', 'success', 2, NULL, 1, 2, 1, NOW()), -(5, '桌面端', 'PC', 'primary', 1, NULL, 1, 3, 1, NOW()), -(6, '安卓', 'ANDROID', 'success', 2, NULL, 1, 3, 1, NOW()), -(7, '小程序', 'XCX', 'warning', 3, NULL, 1, 3, 1, NOW()), -(8, '阿里云', 'alibaba', 'warning', 1, NULL, 1, 4, 1, NOW()), -(9, '腾讯云', 'tencent', 'primary', 2, NULL, 1, 4, 1, NOW()), -(10, '容联云', 'cloopen', 'success', 3, NULL, 1, 4, 1, NOW()); +(1, '产品新闻', '1', 'primary', 1, NULL, 1, 1, 1, NOW()), +(2, '企业动态', '2', 'success', 2, NULL, 1, 1, 1, NOW()), +(3, '桌面端', 'PC', 'primary', 1, NULL, 1, 2, 1, NOW()), +(4, '安卓', 'ANDROID', 'success', 2, NULL, 1, 2, 1, NOW()), +(5, '小程序', 'XCX', 'warning', 3, NULL, 1, 2, 1, NOW()), +(6, '阿里云', 'alibaba', 'warning', 1, NULL, 1, 3, 1, NOW()), +(7, '腾讯云', 'tencent', 'primary', 2, NULL, 1, 3, 1, NOW()), +(8, '容联云', 'cloopen', 'success', 3, NULL, 1, 3, 1, NOW()); -- 初始化默认用户和角色关联数据 INSERT INTO `sys_user_role` diff --git a/continew-webapi/src/main/resources/db/changelog/mysql/main_table.sql b/continew-webapi/src/main/resources/db/changelog/mysql/main_table.sql index e48df635..14732068 100644 --- a/continew-webapi/src/main/resources/db/changelog/mysql/main_table.sql +++ b/continew-webapi/src/main/resources/db/changelog/mysql/main_table.sql @@ -218,31 +218,34 @@ CREATE TABLE IF NOT EXISTS `sys_log` ( CREATE TABLE IF NOT EXISTS `sys_message` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `title` varchar(50) NOT NULL COMMENT '标题', - `content` varchar(255) DEFAULT NULL COMMENT '内容', - `type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(1:系统消息)', - `create_user` bigint(20) DEFAULT NULL COMMENT '创建人', + `content` text DEFAULT NULL COMMENT '内容', + `type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(1:系统消息;2:安全消息)', + `path` varchar(255) DEFAULT NULL COMMENT '跳转路径', + `scope` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '通知范围(1:所有人;2:指定用户)', + `users` json DEFAULT NULL COMMENT '通知用户', `create_time` datetime NOT NULL COMMENT '创建时间', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息表'; -CREATE TABLE IF NOT EXISTS `sys_message_user` ( - `message_id` bigint(20) NOT NULL COMMENT '消息ID', - `user_id` bigint(11) NOT NULL COMMENT '用户ID', - `is_read` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否已读', - `read_time` datetime DEFAULT NULL COMMENT '读取时间', +CREATE TABLE IF NOT EXISTS `sys_message_log` ( + `message_id` bigint(20) NOT NULL COMMENT '消息ID', + `user_id` bigint(20) NOT NULL COMMENT '用户ID', + `read_time` datetime DEFAULT NULL COMMENT '读取时间', PRIMARY KEY (`message_id`, `user_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息和用户关联表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='消息日志表'; CREATE TABLE IF NOT EXISTS `sys_notice` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `title` varchar(150) NOT NULL COMMENT '标题', `content` mediumtext NOT NULL COMMENT '内容', - `type` varchar(30) NOT NULL COMMENT '类型', - `effective_time` datetime DEFAULT NULL COMMENT '生效时间', - `terminate_time` datetime DEFAULT NULL COMMENT '终止时间', + `type` varchar(30) NOT NULL COMMENT '分类', `notice_scope` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '通知范围(1:所有人;2:指定用户)', `notice_users` json DEFAULT NULL COMMENT '通知用户', - `sort` int NOT NULL DEFAULT 999 COMMENT '排序', + `notice_methods` json DEFAULT NULL COMMENT '通知方式(1:登录弹窗;2:系统消息)', + `is_timing` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否定时', + `publish_time` datetime DEFAULT NULL COMMENT '发布时间', + `is_top` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否置顶', + `status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:草稿;2:待发布;3:已发布)', `create_user` bigint(20) NOT NULL COMMENT '创建人', `create_time` datetime NOT NULL COMMENT '创建时间', `update_user` bigint(20) DEFAULT NULL COMMENT '修改人', @@ -252,6 +255,13 @@ CREATE TABLE IF NOT EXISTS `sys_notice` ( INDEX `idx_update_user`(`update_user`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='公告表'; +CREATE TABLE IF NOT EXISTS `sys_notice_log` ( + `notice_id` bigint(20) NOT NULL COMMENT '公告ID', + `user_id` bigint(20) NOT NULL COMMENT '用户ID', + `read_time` datetime DEFAULT NULL COMMENT '读取时间', + PRIMARY KEY (`notice_id`, `user_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='公告日志表'; + CREATE TABLE IF NOT EXISTS `sys_storage` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID', `name` varchar(100) NOT NULL COMMENT '名称', diff --git a/continew-webapi/src/main/resources/db/changelog/postgresql/main_data.sql b/continew-webapi/src/main/resources/db/changelog/postgresql/main_data.sql index a45c17f5..356a4b61 100644 --- a/continew-webapi/src/main/resources/db/changelog/postgresql/main_data.sql +++ b/continew-webapi/src/main/resources/db/changelog/postgresql/main_data.sql @@ -46,10 +46,11 @@ VALUES (1090, '通知公告', 1000, 2, '/system/notice', 'SystemNotice', 'system/notice/index', NULL, 'notification', false, false, false, NULL, 5, 1, 1, NOW()), (1091, '列表', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:list', 1, 1, 1, NOW()), -(1092, '公告详情', 1090, 2, '/system/notice/detail', 'SystemNoticeDetail', 'system/notice/detail/index', NULL, NULL, false, false, true, 'system:notice:get', 2, 1, 1, NOW()), -(1093, '发布公告', 1090, 2, '/system/notice/add', 'SystemNoticeAdd', 'system/notice/add/index', NULL, NULL, false, false, true, 'system:notice:create', 3, 1, 1, NOW()), -(1094, '修改', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:update', 4, 1, 1, NOW()), -(1095, '删除', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:delete', 5, 1, 1, NOW()), +(1092, '详情', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:get', 2, 1, 1, NOW()), +(1093, '查看公告', 1090, 2, '/system/notice/view', 'SystemNoticeView', 'system/notice/view/index', NULL, NULL, false, false, true, 'system:notice:view', 3, 1, 1, NOW()), +(1094, '发布公告', 1090, 2, '/system/notice/add', 'SystemNoticeAdd', 'system/notice/add/index', NULL, NULL, false, false, true, 'system:notice:create', 4, 1, 1, NOW()), +(1095, '修改', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:update', 5, 1, 1, NOW()), +(1096, '删除', 1090, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:notice:delete', 6, 1, 1, NOW()), (1110, '文件管理', 1000, 2, '/system/file', 'SystemFile', 'system/file/index', NULL, 'file', false, false, false, NULL, 6, 1, 1, NOW()), (1111, '列表', 1110, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'system:file:list', 1, 1, 1, NOW()), @@ -215,24 +216,21 @@ VALUES INSERT INTO "sys_dict" ("id", "name", "code", "description", "is_system", "create_user", "create_time") VALUES -(1, '公告类型', 'notice_type', NULL, true, 1, NOW()), -(2, '消息类型', 'message_type', NULL, true, 1, NOW()), -(3, '客户端类型', 'client_type', NULL, true, 1, NOW()), -(4, '短信厂商', 'sms_supplier', NULL, true, 1, NOW()); +(1, '公告分类', 'notice_type', NULL, true, 1, NOW()), +(2, '客户端类型', 'client_type', NULL, true, 1, NOW()), +(3, '短信厂商', 'sms_supplier', NULL, true, 1, NOW()); INSERT INTO "sys_dict_item" ("id", "label", "value", "color", "sort", "description", "status", "dict_id", "create_user", "create_time") VALUES -(1, '通知', '1', 'primary', 1, NULL, 1, 1, 1, NOW()), -(2, '活动', '2', 'success', 2, NULL, 1, 1, 1, NOW()), -(3, '安全消息', '1', 'warning', 1, NULL, 1, 2, 1, NOW()), -(4, '活动消息', '2', 'success', 2, NULL, 1, 2, 1, NOW()), -(5, '桌面端', 'PC', 'primary', 1, NULL, 1, 3, 1, NOW()), -(6, '安卓', 'ANDROID', 'success', 2, NULL, 1, 3, 1, NOW()), -(7, '小程序', 'XCX', 'warning', 3, NULL, 1, 3, 1, NOW()), -(8, '阿里云', 'alibaba', 'warning', 1, NULL, 1, 4, 1, NOW()), -(9, '腾讯云', 'tencent', 'primary', 2, NULL, 1, 4, 1, NOW()), -(10, '容联云', 'cloopen', 'success', 3, NULL, 1, 4, 1, NOW()); +(1, '产品新闻', '1', 'primary', 1, NULL, 1, 1, 1, NOW()), +(2, '企业动态', '2', 'success', 2, NULL, 1, 1, 1, NOW()), +(3, '桌面端', 'PC', 'primary', 1, NULL, 1, 2, 1, NOW()), +(4, '安卓', 'ANDROID', 'success', 2, NULL, 1, 2, 1, NOW()), +(5, '小程序', 'XCX', 'warning', 3, NULL, 1, 2, 1, NOW()), +(6, '阿里云', 'alibaba', 'warning', 1, NULL, 1, 3, 1, NOW()), +(7, '腾讯云', 'tencent', 'primary', 2, NULL, 1, 3, 1, NOW()), +(8, '容联云', 'cloopen', 'success', 3, NULL, 1, 3, 1, NOW()); -- 初始化默认用户和角色关联数据 INSERT INTO "sys_user_role" diff --git a/continew-webapi/src/main/resources/db/changelog/postgresql/main_table.sql b/continew-webapi/src/main/resources/db/changelog/postgresql/main_table.sql index ebf854ca..b7a5956e 100644 --- a/continew-webapi/src/main/resources/db/changelog/postgresql/main_table.sql +++ b/continew-webapi/src/main/resources/db/changelog/postgresql/main_table.sql @@ -360,43 +360,47 @@ COMMENT ON TABLE "sys_log" IS '系统日志表'; CREATE TABLE IF NOT EXISTS "sys_message" ( "id" int8 NOT NULL, "title" varchar(50) NOT NULL, - "content" varchar(255) DEFAULT NULL, + "content" text DEFAULT NULL, "type" int2 NOT NULL DEFAULT 1, - "create_user" int8 DEFAULT NULL, + "path" varchar(255) DEFAULT NULL, + "scope" int2 NOT NULL DEFAULT 1, + "users" json DEFAULT NULL, "create_time" timestamp NOT NULL, PRIMARY KEY ("id") ); COMMENT ON COLUMN "sys_message"."id" IS 'ID'; COMMENT ON COLUMN "sys_message"."title" IS '标题'; COMMENT ON COLUMN "sys_message"."content" IS '内容'; -COMMENT ON COLUMN "sys_message"."type" IS '类型(1:系统消息)'; -COMMENT ON COLUMN "sys_message"."create_user" IS '创建人'; +COMMENT ON COLUMN "sys_message"."type" IS '类型(1:系统消息;2:安全消息)'; +COMMENT ON COLUMN "sys_message"."path" IS '跳转路径'; +COMMENT ON COLUMN "sys_message"."scope" IS '通知范围(1:所有人;2:指定用户)'; +COMMENT ON COLUMN "sys_message"."users" IS '通知用户'; COMMENT ON COLUMN "sys_message"."create_time" IS '创建时间'; COMMENT ON TABLE "sys_message" IS '消息表'; -CREATE TABLE IF NOT EXISTS "sys_message_user" ( +CREATE TABLE IF NOT EXISTS "sys_message_log" ( "message_id" int8 NOT NULL, "user_id" int8 NOT NULL, - "is_read" bool NOT NULL DEFAULT false, "read_time" timestamp DEFAULT NULL, PRIMARY KEY ("message_id", "user_id") ); -COMMENT ON COLUMN "sys_message_user"."message_id" IS '消息ID'; -COMMENT ON COLUMN "sys_message_user"."user_id" IS '用户ID'; -COMMENT ON COLUMN "sys_message_user"."is_read" IS '是否已读'; -COMMENT ON COLUMN "sys_message_user"."read_time" IS '读取时间'; -COMMENT ON TABLE "sys_message_user" IS '消息和用户关联表'; +COMMENT ON COLUMN "sys_message_log"."message_id" IS '消息ID'; +COMMENT ON COLUMN "sys_message_log"."user_id" IS '用户ID'; +COMMENT ON COLUMN "sys_message_log"."read_time" IS '读取时间'; +COMMENT ON TABLE "sys_message_log" IS '消息日志表'; CREATE TABLE IF NOT EXISTS "sys_notice" ( "id" int8 NOT NULL, "title" varchar(150) NOT NULL, "content" text NOT NULL, "type" varchar(30) NOT NULL, - "effective_time" timestamp DEFAULT NULL, - "terminate_time" timestamp DEFAULT NULL, "notice_scope" int2 NOT NULL DEFAULT 1, "notice_users" json DEFAULT NULL, - "sort" int4 NOT NULL DEFAULT 999, + "notice_methods" json DEFAULT NULL, + "is_timing" bool NOT NULL DEFAULT false, + "publish_time" timestamp DEFAULT NULL, + "is_top" bool NOT NULL DEFAULT false, + "status" int2 NOT NULL DEFAULT 1, "create_user" int8 NOT NULL, "create_time" timestamp NOT NULL, "update_user" int8 DEFAULT NULL, @@ -408,18 +412,31 @@ CREATE INDEX "idx_notice_update_user" ON "sys_notice" ("update_user"); COMMENT ON COLUMN "sys_notice"."id" IS 'ID'; COMMENT ON COLUMN "sys_notice"."title" IS '标题'; COMMENT ON COLUMN "sys_notice"."content" IS '内容'; -COMMENT ON COLUMN "sys_notice"."type" IS '类型'; -COMMENT ON COLUMN "sys_notice"."effective_time" IS '生效时间'; -COMMENT ON COLUMN "sys_notice"."terminate_time" IS '终止时间'; +COMMENT ON COLUMN "sys_notice"."type" IS '分类'; COMMENT ON COLUMN "sys_notice"."notice_scope" IS '通知范围(1:所有人;2:指定用户)'; COMMENT ON COLUMN "sys_notice"."notice_users" IS '通知用户'; -COMMENT ON COLUMN "sys_notice"."sort" IS '排序'; +COMMENT ON COLUMN "sys_notice"."notice_methods" IS '通知方式(1:登录弹窗;2:系统消息)'; +COMMENT ON COLUMN "sys_notice"."is_timing" IS '是否定时'; +COMMENT ON COLUMN "sys_notice"."publish_time" IS '发布时间'; +COMMENT ON COLUMN "sys_notice"."is_top" IS '是否置顶'; +COMMENT ON COLUMN "sys_notice"."status" IS '状态(1:草稿;2:待发布;3:已发布)'; COMMENT ON COLUMN "sys_notice"."create_user" IS '创建人'; COMMENT ON COLUMN "sys_notice"."create_time" IS '创建时间'; COMMENT ON COLUMN "sys_notice"."update_user" IS '修改人'; COMMENT ON COLUMN "sys_notice"."update_time" IS '修改时间'; COMMENT ON TABLE "sys_notice" IS '公告表'; +CREATE TABLE IF NOT EXISTS "sys_notice_log" ( + "notice_id" int8 NOT NULL, + "user_id" int8 NOT NULL, + "read_time" timestamp DEFAULT NULL, + PRIMARY KEY ("notice_id", "user_id") +); +COMMENT ON COLUMN "sys_notice_log"."notice_id" IS '消息ID'; +COMMENT ON COLUMN "sys_notice_log"."user_id" IS '用户ID'; +COMMENT ON COLUMN "sys_notice_log"."read_time" IS '读取时间'; +COMMENT ON TABLE "sys_notice_log" IS '公告日志表'; + CREATE TABLE IF NOT EXISTS "sys_storage" ( "id" int8 NOT NULL, "name" varchar(100) NOT NULL,