diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java index f5fdfac2b..d27b98e2a 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/AuthController.java @@ -12,12 +12,13 @@ import me.zhyd.oauth.request.AuthRequest; import me.zhyd.oauth.utils.AuthStateUtils; import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.domain.R; -import org.dromara.common.core.domain.dto.PushPayload; +import org.dromara.common.core.domain.dto.PushPayloadDTO; import org.dromara.common.core.domain.model.LoginBody; import org.dromara.common.core.domain.model.RegisterBody; import org.dromara.common.core.domain.model.SocialLoginBody; import org.dromara.common.core.enums.PushSourceEnum; import org.dromara.common.core.enums.PushTypeEnum; +import org.dromara.common.core.service.MessageService; import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.MessageUtils; import org.dromara.common.core.utils.StringUtils; @@ -26,7 +27,6 @@ import org.dromara.common.encrypt.annotation.ApiEncrypt; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.redis.annotation.RateLimiter; import org.dromara.common.redis.enums.LimitType; -import org.dromara.common.push.helper.PushHelper; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.social.config.properties.SocialLoginConfigProperties; import org.dromara.common.social.config.properties.SocialProperties; @@ -66,6 +66,7 @@ public class AuthController { private final ISysSocialService socialUserService; private final ISysClientService clientService; private final ScheduledExecutorService scheduledExecutorService; + private final MessageService messageService; /** @@ -95,9 +96,9 @@ public class AuthController { Long userId = LoginHelper.getUserId(); scheduledExecutorService.schedule(() -> { - PushHelper.publishMessage( + messageService.publishMessage( List.of(userId), - PushPayload.of( + PushPayloadDTO.of( PushTypeEnum.MESSAGE, PushSourceEnum.BACKEND, DateUtils.getTodayHour(new Date()) + "好,欢迎登录 RuoYi-Vue-Plus 后台管理系统", diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/PushPayload.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/PushPayloadDTO.java similarity index 70% rename from ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/PushPayload.java rename to ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/PushPayloadDTO.java index 882f2fb3c..6abd00dc7 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/PushPayload.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/dto/PushPayloadDTO.java @@ -14,11 +14,16 @@ import java.io.Serializable; * @author Lion Li */ @Data -public class PushPayload implements Serializable { +public class PushPayloadDTO implements Serializable { @Serial private static final long serialVersionUID = 1L; + /** + * 消息记录ID + */ + private Long messageId; + /** * 消息类型 */ @@ -49,8 +54,8 @@ public class PushPayload implements Serializable { */ private Long timestamp; - public static PushPayload of(String type, String source, String message, Object data) { - PushPayload payload = new PushPayload(); + public static PushPayloadDTO of(String type, String source, String message, Object data) { + PushPayloadDTO payload = new PushPayloadDTO(); payload.setType(StringUtils.defaultIfBlank(type, PushTypeEnum.MESSAGE.getType())); payload.setSource(StringUtils.defaultIfBlank(source, PushSourceEnum.BACKEND.getSource())); payload.setMessage(message); @@ -59,7 +64,7 @@ public class PushPayload implements Serializable { return payload; } - public static PushPayload of(PushTypeEnum type, PushSourceEnum source, String message, Object data) { + public static PushPayloadDTO of(PushTypeEnum type, PushSourceEnum source, String message, Object data) { return of( type == null ? null : type.getType(), source == null ? null : source.getSource(), @@ -68,8 +73,8 @@ public class PushPayload implements Serializable { ); } - public static PushPayload of(PushTypeEnum type, PushSourceEnum source, String message, Object data, String path) { - PushPayload payload = of(type, source, message, data); + public static PushPayloadDTO of(PushTypeEnum type, PushSourceEnum source, String message, Object data, String path) { + PushPayloadDTO payload = of(type, source, message, data); payload.setPath(path); return payload; } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/MessageService.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/MessageService.java new file mode 100644 index 000000000..275b99aad --- /dev/null +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/service/MessageService.java @@ -0,0 +1,27 @@ +package org.dromara.common.core.service; + +import org.dromara.common.core.domain.dto.PushPayloadDTO; + +import java.util.List; + +/** + * 通用 消息服务 + * + * @author Lion Li + */ +public interface MessageService { + + void sendMessage(Long userId, String message); + + void sendMessage(String message); + + void sendMessage(Long userId, PushPayloadDTO payload); + + void sendMessage(PushPayloadDTO payload); + + void publishMessage(List userIds, PushPayloadDTO payload); + + void publishAll(String message); + + void publishAll(PushPayloadDTO payload); +} diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/controller/SseController.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/controller/SseController.java index ce42857a8..d89433345 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/controller/SseController.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/controller/SseController.java @@ -63,7 +63,7 @@ public class SseController implements DisposableBean { // public R send(Long userId, String msg) { // PushDTO dto = new PushDTO(); // dto.setUserIds(List.of(userId)); -// dto.setPayload(PushPayload.of("message", "backend", msg, null)); +// dto.setPayload(PushPayloadDTO.of("message", "backend", msg, null)); // sessionManager.publishMessage(dto); // return R.ok(); // } diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/PushSessionManager.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/PushSessionManager.java index 384a83ec7..bbe926ff1 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/PushSessionManager.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/PushSessionManager.java @@ -1,6 +1,6 @@ package org.dromara.common.push.core; -import org.dromara.common.core.domain.dto.PushPayload; +import org.dromara.common.core.domain.dto.PushPayloadDTO; import org.dromara.common.push.dto.PushDTO; import java.util.function.Consumer; @@ -14,11 +14,11 @@ public interface PushSessionManager { void subscribeMessage(Consumer consumer); - void sendMessage(Long userId, PushPayload payload); + void sendMessage(Long userId, PushPayloadDTO payload); - void sendMessage(PushPayload payload); + void sendMessage(PushPayloadDTO payload); void publishMessage(PushDTO pushDTO); - void publishAll(PushPayload payload); + void publishAll(PushPayloadDTO payload); } diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java index 042e0759d..649f1dff9 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java @@ -3,7 +3,7 @@ package org.dromara.common.push.core; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import lombok.extern.slf4j.Slf4j; -import org.dromara.common.core.domain.dto.PushPayload; +import org.dromara.common.core.domain.dto.PushPayloadDTO; import org.dromara.common.push.constant.MessageConstants; import org.dromara.common.push.dto.PushDTO; import org.dromara.common.core.utils.SpringUtils; @@ -194,7 +194,7 @@ public class SseEmitterSessionManager implements PushSessionManager { * @param payload 要发送的消息体 */ @Override - public void sendMessage(Long userId, PushPayload payload) { + public void sendMessage(Long userId, PushPayloadDTO payload) { sendMessage(userId, JsonUtils.toJsonString(payload)); } @@ -228,7 +228,7 @@ public class SseEmitterSessionManager implements PushSessionManager { * @param payload 要发送的消息体 */ @Override - public void sendMessage(PushPayload payload) { + public void sendMessage(PushPayloadDTO payload) { sendMessage(JsonUtils.toJsonString(payload)); } @@ -253,7 +253,7 @@ public class SseEmitterSessionManager implements PushSessionManager { * @param message 要发布的消息内容 */ public void publishAll(String message) { - publishAll(PushPayload.of("message", "backend", message, null)); + publishAll(PushPayloadDTO.of("message", "backend", message, null)); } /** @@ -262,7 +262,7 @@ public class SseEmitterSessionManager implements PushSessionManager { * @param payload 要发布的消息体 */ @Override - public void publishAll(PushPayload payload) { + public void publishAll(PushPayloadDTO payload) { PushDTO dto = new PushDTO(); dto.setPayload(payload); RedisUtils.publish(MessageConstants.MESSAGE_TOPIC, dto, consumer -> { diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java index 30f34df79..1bde966e8 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java @@ -3,7 +3,7 @@ package org.dromara.common.push.core; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import lombok.extern.slf4j.Slf4j; -import org.dromara.common.core.domain.dto.PushPayload; +import org.dromara.common.core.domain.dto.PushPayloadDTO; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.push.dto.PushDTO; @@ -90,7 +90,7 @@ public class WebSocketSessionManager implements PushSessionManager { } @Override - public void sendMessage(Long userId, PushPayload payload) { + public void sendMessage(Long userId, PushPayloadDTO payload) { if (payload == null) { return; } @@ -113,7 +113,7 @@ public class WebSocketSessionManager implements PushSessionManager { } @Override - public void sendMessage(PushPayload payload) { + public void sendMessage(PushPayloadDTO payload) { USER_TOKEN_SESSIONS.keySet().forEach(userId -> sendMessage(userId, payload)); } @@ -128,7 +128,7 @@ public class WebSocketSessionManager implements PushSessionManager { } @Override - public void publishAll(PushPayload payload) { + public void publishAll(PushPayloadDTO payload) { PushDTO dto = new PushDTO(); dto.setPayload(payload); publishMessage(dto); diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/dto/PushDTO.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/dto/PushDTO.java index 8a31df0b5..fcd47bcea 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/dto/PushDTO.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/dto/PushDTO.java @@ -1,7 +1,7 @@ package org.dromara.common.push.dto; import lombok.Data; -import org.dromara.common.core.domain.dto.PushPayload; +import org.dromara.common.core.domain.dto.PushPayloadDTO; import java.io.Serial; import java.io.Serializable; @@ -26,5 +26,5 @@ public class PushDTO implements Serializable { /** * 推送消息体。 */ - private PushPayload payload; + private PushPayloadDTO payload; } diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/handler/PlusWebSocketHandler.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/handler/PlusWebSocketHandler.java index f704c9245..1251d238d 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/handler/PlusWebSocketHandler.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/handler/PlusWebSocketHandler.java @@ -3,7 +3,7 @@ package org.dromara.common.push.handler; import cn.hutool.core.util.ObjectUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.dromara.common.core.domain.dto.PushPayload; +import org.dromara.common.core.domain.dto.PushPayloadDTO; import org.dromara.common.core.domain.model.LoginUser; import org.dromara.common.core.enums.PushSourceEnum; import org.dromara.common.core.enums.PushTypeEnum; @@ -61,7 +61,7 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler { } PushDTO dto = new PushDTO(); dto.setUserIds(List.of(loginUser.getUserId())); - dto.setPayload(PushPayload.of( + dto.setPayload(PushPayloadDTO.of( PushTypeEnum.CUSTOM, PushSourceEnum.CLIENT, message.getPayload(), diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/helper/PushHelper.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/helper/PushHelper.java index dedf5e462..ed229542a 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/helper/PushHelper.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/helper/PushHelper.java @@ -2,7 +2,7 @@ package org.dromara.common.push.helper; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import org.dromara.common.core.domain.dto.PushPayload; +import org.dromara.common.core.domain.dto.PushPayloadDTO; import org.dromara.common.core.enums.PushSourceEnum; import org.dromara.common.core.enums.PushTypeEnum; import org.dromara.common.core.utils.SpringUtils; @@ -27,21 +27,21 @@ public class PushHelper { sendMessage(buildMessage(message)); } - public static void sendMessage(Long userId, PushPayload payload) { + public static void sendMessage(Long userId, PushPayloadDTO payload) { if (!isEnabled()) { return; } getSessionManager().sendMessage(userId, payload); } - public static void sendMessage(PushPayload payload) { + public static void sendMessage(PushPayloadDTO payload) { if (!isEnabled()) { return; } getSessionManager().sendMessage(payload); } - public static void publishMessage(List userIds, PushPayload payload) { + public static void publishMessage(List userIds, PushPayloadDTO payload) { PushDTO dto = new PushDTO(); dto.setUserIds(userIds); dto.setPayload(payload); @@ -59,7 +59,7 @@ public class PushHelper { publishAll(buildMessage(message)); } - public static void publishAll(PushPayload payload) { + public static void publishAll(PushPayloadDTO payload) { if (!isEnabled()) { return; } @@ -74,7 +74,7 @@ public class PushHelper { return SpringUtils.getBean(PushSessionManager.class); } - private static PushPayload buildMessage(String message) { - return PushPayload.of(PushTypeEnum.MESSAGE, PushSourceEnum.BACKEND, message, null); + private static PushPayloadDTO buildMessage(String message) { + return PushPayloadDTO.of(PushTypeEnum.MESSAGE, PushSourceEnum.BACKEND, message, null); } } diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WebSocketController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WebSocketController.java index 92c738a71..d3866cc25 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WebSocketController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/WebSocketController.java @@ -1,10 +1,10 @@ package org.dromara.demo.controller; import org.dromara.common.core.domain.R; -import org.dromara.common.core.domain.dto.PushPayload; +import org.dromara.common.core.domain.dto.PushPayloadDTO; import org.dromara.common.core.enums.PushSourceEnum; import org.dromara.common.core.enums.PushTypeEnum; -import org.dromara.common.push.helper.PushHelper; +import org.dromara.common.core.service.MessageService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; @@ -24,6 +24,8 @@ import java.util.List; @Slf4j public class WebSocketController { + private final MessageService messageService; + /** * 发布消息 * @@ -32,16 +34,16 @@ public class WebSocketController { */ @GetMapping("/send") public R send(Long userId, String message) { - PushPayload payload = PushPayload.of( + PushPayloadDTO payload = PushPayloadDTO.of( PushTypeEnum.MESSAGE, PushSourceEnum.BACKEND, message, null ); if (userId == null) { - PushHelper.publishAll(payload); + messageService.publishAll(payload); } else { - PushHelper.publishMessage(List.of(userId), payload); + messageService.publishMessage(List.of(userId), payload); } return R.ok("操作成功"); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMessageController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMessageController.java new file mode 100644 index 000000000..c13a74013 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysMessageController.java @@ -0,0 +1,34 @@ +package org.dromara.system.controller.system; + +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.dromara.system.domain.vo.SysMessageBoxVo; +import org.dromara.system.service.ISysMessageService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 消息记录控制器 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/message") +public class SysMessageController extends BaseController { + + private final ISysMessageService messageService; + + /** + * 查询当前用户消息盒子数据 + * + * @return 消息盒子数据 + */ + @GetMapping("/box") + public R getBox() { + return R.ok(messageService.queryMessageBox(LoginHelper.getUserId())); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java index 2b400e260..d68a0c163 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysNoticeController.java @@ -4,14 +4,14 @@ import cn.dev33.satoken.annotation.SaCheckPermission; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.domain.R; -import org.dromara.common.core.domain.dto.PushPayload; +import org.dromara.common.core.domain.dto.PushPayloadDTO; import org.dromara.common.core.enums.PushSourceEnum; import org.dromara.common.core.enums.PushTypeEnum; import org.dromara.common.core.service.DictService; +import org.dromara.common.core.service.MessageService; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; -import org.dromara.common.push.helper.PushHelper; import org.dromara.common.redis.annotation.RepeatSubmit; import org.dromara.common.web.core.BaseController; import org.dromara.system.domain.bo.SysNoticeBo; @@ -36,6 +36,7 @@ public class SysNoticeController extends BaseController { private final ISysNoticeService noticeService; private final DictService dictService; + private final MessageService messageService; /** * 分页查询通知公告列表。 @@ -83,7 +84,9 @@ public class SysNoticeController extends BaseController { data.put("noticeTypeLabel", type); data.put("noticeTitle", notice.getNoticeTitle()); data.put("noticeId", notice.getNoticeId()); - PushHelper.publishAll(PushPayload.of( + data.put("noticeContent", notice.getNoticeContent()); + data.put("status", notice.getStatus()); + messageService.publishAll(PushPayloadDTO.of( PushTypeEnum.NOTICE, PushSourceEnum.NOTICE, "[" + type + "] " + notice.getNoticeTitle(), diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMessage.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMessage.java new file mode 100644 index 000000000..176e187f9 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysMessage.java @@ -0,0 +1,69 @@ +package org.dromara.system.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 消息记录表 sys_message + * + * @author Lion Li + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("sys_message") +public class SysMessage extends BaseEntity { + + /** + * 消息ID + */ + @TableId(value = "message_id") + private Long messageId; + + /** + * 消息分组 + */ + private String category; + + /** + * 消息类型 + */ + private String type; + + /** + * 消息来源 + */ + private String source; + + /** + * 标题 + */ + private String title; + + /** + * 摘要消息 + */ + private String message; + + /** + * 详细内容 + */ + private String content; + + /** + * 扩展数据 JSON + */ + private String dataJson; + + /** + * 前端跳转路径 + */ + private String path; + + /** + * 目标用户ID串,0 表示全局 + */ + private String sendUserIds; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageBoxVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageBoxVo.java new file mode 100644 index 000000000..c466aa707 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageBoxVo.java @@ -0,0 +1,35 @@ +package org.dromara.system.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 消息盒子视图对象 + * + * @author Lion Li + */ +@Data +public class SysMessageBoxVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 系统消息 + */ + private List systemList = new ArrayList<>(); + + /** + * 通知公告消息 + */ + private List noticeList = new ArrayList<>(); + + /** + * 工作流消息 + */ + private List workflowList = new ArrayList<>(); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageVo.java new file mode 100644 index 000000000..161ca08fe --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysMessageVo.java @@ -0,0 +1,72 @@ +package org.dromara.system.domain.vo; + +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.system.domain.SysMessage; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 消息记录视图对象 sys_message + * + * @author Lion Li + */ +@Data +@AutoMapper(target = SysMessage.class) +public class SysMessageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 消息ID + */ + private Long messageId; + + /** + * 消息分组 + */ + private String category; + + /** + * 消息类型 + */ + private String type; + + /** + * 消息来源 + */ + private String source; + + /** + * 标题 + */ + private String title; + + /** + * 摘要消息 + */ + private String message; + + /** + * 详细内容 + */ + private String content; + + /** + * 扩展数据 + */ + private Object data; + + /** + * 前端跳转路径 + */ + private String path; + + /** + * 创建时间 + */ + private Date createTime; +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMessageMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMessageMapper.java new file mode 100644 index 000000000..39cfff7c9 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMessageMapper.java @@ -0,0 +1,13 @@ +package org.dromara.system.mapper; + +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.system.domain.SysMessage; +import org.dromara.system.domain.vo.SysMessageVo; + +/** + * 消息记录Mapper接口 + * + * @author Lion Li + */ +public interface SysMessageMapper extends BaseMapperPlus { +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMessageService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMessageService.java new file mode 100644 index 000000000..d4acaed3e --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMessageService.java @@ -0,0 +1,91 @@ +package org.dromara.system.service; + +import org.dromara.common.core.domain.dto.PushPayloadDTO; +import org.dromara.system.domain.vo.SysMessageBoxVo; + +import java.util.List; + +/** + * 消息记录服务接口 + * + * @author Lion Li + */ +public interface ISysMessageService { + + /** + * 查询当前用户消息盒子数据 + * + * @param userId 用户ID + * @return 消息盒子数据 + */ + SysMessageBoxVo queryMessageBox(Long userId); + + /** + * 发送指定用户文本消息 + * + * @param userId 目标用户 + * @param message 文本消息 + */ + void sendMessage(Long userId, String message); + + /** + * 广播文本消息 + * + * @param message 文本消息 + */ + void sendMessage(String message); + + /** + * 发送指定用户消息 + * + * @param userId 目标用户 + * @param payload 推送消息体 + */ + void sendMessage(Long userId, PushPayloadDTO payload); + + /** + * 广播消息 + * + * @param payload 推送消息体 + */ + void sendMessage(PushPayloadDTO payload); + + /** + * 发布指定用户消息 + * + * @param userIds 用户ID列表 + * @param payload 推送消息体 + */ + void publishMessage(List userIds, PushPayloadDTO payload); + + /** + * 发布广播文本消息 + * + * @param message 文本消息 + */ + void publishAll(String message); + + /** + * 发布广播消息 + * + * @param payload 推送消息体 + */ + void publishAll(PushPayloadDTO payload); + + /** + * 记录全局消息 + * + * @param payload 推送消息体 + * @return 回填消息ID后的推送消息体 + */ + PushPayloadDTO storeAll(PushPayloadDTO payload); + + /** + * 记录指定用户消息 + * + * @param userIds 用户ID列表 + * @param payload 推送消息体 + * @return 回填消息ID后的推送消息体 + */ + PushPayloadDTO storeUsers(List userIds, PushPayloadDTO payload); +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java new file mode 100644 index 000000000..c3fcc4625 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java @@ -0,0 +1,194 @@ +package org.dromara.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.dto.PushPayloadDTO; +import org.dromara.common.core.enums.PushSourceEnum; +import org.dromara.common.core.enums.PushTypeEnum; +import org.dromara.common.core.service.MessageService; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.common.mybatis.helper.DataBaseHelper; +import org.dromara.common.mybatis.utils.IdGeneratorUtil; +import org.dromara.common.push.helper.PushHelper; +import org.dromara.system.domain.SysMessage; +import org.dromara.system.domain.vo.SysMessageBoxVo; +import org.dromara.system.domain.vo.SysMessageVo; +import org.dromara.system.mapper.SysMessageMapper; +import org.dromara.system.service.ISysMessageService; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * 消息记录服务实现 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Service +public class SysMessageServiceImpl implements ISysMessageService, MessageService { + + private static final String GLOBAL_USER_IDS = "0"; + private static final String CATEGORY_SYSTEM = "system"; + private static final String CATEGORY_NOTICE = "notice"; + private static final String CATEGORY_WORKFLOW = "workflow"; + private static final int BOX_LIMIT = 100; + private static final long BOX_DAYS = 30L; + + private final SysMessageMapper baseMapper; + + @Override + public SysMessageBoxVo queryMessageBox(Long userId) { + SysMessageBoxVo box = new SysMessageBoxVo(); + box.setSystemList(selectMessageList(CATEGORY_SYSTEM, userId)); + box.setNoticeList(selectMessageList(CATEGORY_NOTICE, userId)); + box.setWorkflowList(selectMessageList(CATEGORY_WORKFLOW, userId)); + return box; + } + + @Override + public void sendMessage(Long userId, String message) { + PushHelper.sendMessage(userId, buildDefaultMessage(message)); + } + + @Override + public void sendMessage(String message) { + PushHelper.sendMessage(buildDefaultMessage(message)); + } + + @Override + public void sendMessage(Long userId, PushPayloadDTO payload) { + PushHelper.sendMessage(userId, payload); + } + + @Override + public void sendMessage(PushPayloadDTO payload) { + PushHelper.sendMessage(payload); + } + + @Override + public void publishMessage(List userIds, PushPayloadDTO payload) { + PushHelper.publishMessage(userIds, storeUsers(userIds, payload)); + } + + @Override + public void publishAll(String message) { + publishAll(buildDefaultMessage(message)); + } + + @Override + public void publishAll(PushPayloadDTO payload) { + PushHelper.publishAll(storeAll(payload)); + } + + @Override + public PushPayloadDTO storeAll(PushPayloadDTO payload) { + return storeMessage(null, payload); + } + + @Override + public PushPayloadDTO storeUsers(List userIds, PushPayloadDTO payload) { + return storeMessage(userIds, payload); + } + + private PushPayloadDTO storeMessage(List userIds, PushPayloadDTO payload) { + if (!supportsMessageBox(payload)) { + return payload; + } + SysMessage message = buildMessage(userIds, payload); + baseMapper.insert(message); + payload.setMessageId(message.getMessageId()); + return payload; + } + + private List selectMessageList(String category, Long userId) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(SysMessage::getCategory, category); + lqw.ge(SysMessage::getCreateTime, new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(BOX_DAYS))); + lqw.and(wrapper -> wrapper.eq(SysMessage::getSendUserIds, GLOBAL_USER_IDS) + .or() + .apply(DataBaseHelper.findInSet(userId, "send_user_ids"))); + lqw.orderByDesc(SysMessage::getCreateTime, SysMessage::getMessageId); + List list = baseMapper.selectList(new Page<>(1, BOX_LIMIT, false), lqw); + return list.stream().map(this::buildVo).toList(); + } + + private SysMessage buildMessage(List userIds, PushPayloadDTO payload) { + SysMessage message = new SysMessage(); + message.setMessageId(payload.getMessageId() == null ? IdGeneratorUtil.nextLongId() : payload.getMessageId()); + message.setCategory(resolveCategory(payload)); + message.setType(payload.getType()); + message.setSource(payload.getSource()); + message.setTitle(resolveTitle(payload)); + message.setMessage(payload.getMessage()); + message.setContent(resolveContent(payload)); + message.setDataJson(JsonUtils.toJsonString(payload.getData())); + message.setPath(payload.getPath()); + message.setSendUserIds(CollUtil.isEmpty(userIds) ? GLOBAL_USER_IDS : StringUtils.joinComma(userIds)); + return message; + } + + private SysMessageVo buildVo(SysMessage entity) { + SysMessageVo vo = MapstructUtils.convert(entity, SysMessageVo.class); + vo.setData(parseData(entity.getDataJson())); + return vo; + } + + private boolean supportsMessageBox(PushPayloadDTO payload) { + if (payload == null) { + return false; + } + if (StringUtils.equalsAny(payload.getType(), PushTypeEnum.MESSAGE.getType(), PushTypeEnum.NOTICE.getType())) { + return !StringUtils.equalsAny(payload.getType(), PushTypeEnum.LLM.getType()) + && !StringUtils.equalsAny(payload.getSource(), PushSourceEnum.LLM.getSource()); + } + return false; + } + + private String resolveCategory(PushPayloadDTO payload) { + if (StringUtils.equalsAny(payload.getType(), PushTypeEnum.NOTICE.getType()) + || StringUtils.equalsAny(payload.getSource(), PushSourceEnum.NOTICE.getSource())) { + return CATEGORY_NOTICE; + } + if (StringUtils.equalsAny(payload.getSource(), PushSourceEnum.WORKFLOW.getSource())) { + return CATEGORY_WORKFLOW; + } + return CATEGORY_SYSTEM; + } + + private String resolveTitle(PushPayloadDTO payload) { + return switch (resolveCategory(payload)) { + case CATEGORY_NOTICE -> "通知公告消息"; + case CATEGORY_WORKFLOW -> "工作流消息"; + default -> "系统消息"; + }; + } + + private String resolveContent(PushPayloadDTO payload) { + Object data = payload.getData(); + if (data instanceof Map map) { + return Convert.toStr(map.get("noticeContent")); + } + return null; + } + + private Object parseData(String dataJson) { + if (StringUtils.isBlank(dataJson)) { + return null; + } + return JsonUtils.parseObject(dataJson, Object.class); + } + + private PushPayloadDTO buildDefaultMessage(String message) { + return PushPayloadDTO.of(PushTypeEnum.MESSAGE, PushSourceEnum.BACKEND, message, null); + } +} diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMessageMapper.xml b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMessageMapper.xml new file mode 100644 index 000000000..9e468e343 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/resources/mapper/system/SysMessageMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index d10a05717..cfccc7661 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -4,16 +4,16 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.dromara.common.core.domain.dto.PushPayload; +import org.dromara.common.core.domain.dto.PushPayloadDTO; import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.enums.PushSourceEnum; import org.dromara.common.core.enums.PushTypeEnum; import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.service.MessageService; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mail.utils.MailUtils; -import org.dromara.common.push.helper.PushHelper; import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.entity.Node; import org.dromara.warm.flow.orm.entity.FlowTask; @@ -41,6 +41,7 @@ import java.util.Set; public class FlwCommonServiceImpl implements IFlwCommonService { private static final String DEFAULT_SUBJECT = "单据审批提醒"; + private final MessageService messageService; /** * 根据流程实例发送消息给当前处理人 @@ -94,7 +95,7 @@ public class FlwCommonServiceImpl implements IFlwCommonService { try { switch (messageTypeEnum) { case SYSTEM_MESSAGE -> { - PushHelper.publishMessage(userIds, PushPayload.of( + messageService.publishMessage(userIds, PushPayloadDTO.of( PushTypeEnum.MESSAGE, PushSourceEnum.WORKFLOW, message, diff --git a/script/sql/oracle/oracle_ry_vue_5.X.sql b/script/sql/oracle/oracle_ry_vue_5.X.sql index f954b0f31..50bd7dfc8 100644 --- a/script/sql/oracle/oracle_ry_vue_5.X.sql +++ b/script/sql/oracle/oracle_ry_vue_5.X.sql @@ -925,7 +925,50 @@ insert into sys_notice values('2', '维护通知:2018-07-01 系统凌晨维护 -- ---------------------------- --- 18、代码生成业务表 +-- 18、消息记录表 +-- ---------------------------- +create table sys_message ( + message_id number(20) not null, + category varchar2(20) not null, + type varchar2(20) not null, + source varchar2(20) not null, + title varchar2(100) default '', + message varchar2(500) default '', + content clob default null, + data_json clob default null, + path varchar2(500) default null, + send_user_ids varchar2(2000) default '0' not null, + create_dept number(20) default null, + create_by number(20) default null, + create_time date, + update_by number(20) default null, + update_time date +); + +alter table sys_message add constraint pk_sys_message primary key (message_id); + +create index idx_sys_message_category_time on sys_message(category, create_time); + +comment on table sys_message is '消息记录表'; +comment on column sys_message.message_id is '消息ID'; +comment on column sys_message.category is '消息分组(system/notice/workflow)'; +comment on column sys_message.type is '消息类型'; +comment on column sys_message.source is '消息来源'; +comment on column sys_message.title is '标题'; +comment on column sys_message.message is '摘要消息'; +comment on column sys_message.content is '详细内容'; +comment on column sys_message.data_json is '扩展数据JSON'; +comment on column sys_message.path is '前端跳转路径'; +comment on column sys_message.send_user_ids is '目标用户ID串,0表示全局'; +comment on column sys_message.create_dept is '创建部门'; +comment on column sys_message.create_by is '创建者'; +comment on column sys_message.create_time is '创建时间'; +comment on column sys_message.update_by is '更新者'; +comment on column sys_message.update_time is '更新时间'; + + +-- ---------------------------- +-- 19、代码生成业务表 -- ---------------------------- create table gen_table ( table_id number(20) not null, @@ -980,7 +1023,7 @@ comment on column gen_table.remark is '备注'; -- ---------------------------- --- 19、代码生成业务表字段 +-- 20、代码生成业务表字段 -- ---------------------------- create table gen_table_column ( column_id number(20) not null, diff --git a/script/sql/postgres/postgres_ry_vue_5.X.sql b/script/sql/postgres/postgres_ry_vue_5.X.sql index 284cd5474..e757aa381 100644 --- a/script/sql/postgres/postgres_ry_vue_5.X.sql +++ b/script/sql/postgres/postgres_ry_vue_5.X.sql @@ -923,7 +923,50 @@ insert into sys_notice values('2', '维护通知:2018-07-01 系统凌晨维护 -- ---------------------------- --- 18、代码生成业务表 +-- 18、消息记录表 +-- ---------------------------- +create table if not exists sys_message +( + message_id int8, + category varchar(20) not null, + type varchar(20) not null, + source varchar(20) not null, + title varchar(100) default ''::varchar, + message varchar(500) default ''::varchar, + content text, + data_json text, + path varchar(500) default null::varchar, + send_user_ids varchar(2000) not null default '0'::varchar, + create_dept int8, + create_by int8, + create_time timestamp, + update_by int8, + update_time timestamp, + constraint sys_message_pk primary key (message_id) +); + +create index if not exists idx_sys_message_category_time on sys_message (category, create_time); + +comment on table sys_message is '消息记录表'; +comment on column sys_message.message_id is '消息ID'; +comment on column sys_message.category is '消息分组(system/notice/workflow)'; +comment on column sys_message.type is '消息类型'; +comment on column sys_message.source is '消息来源'; +comment on column sys_message.title is '标题'; +comment on column sys_message.message is '摘要消息'; +comment on column sys_message.content is '详细内容'; +comment on column sys_message.data_json is '扩展数据JSON'; +comment on column sys_message.path is '前端跳转路径'; +comment on column sys_message.send_user_ids is '目标用户ID串,0表示全局'; +comment on column sys_message.create_dept is '创建部门'; +comment on column sys_message.create_by is '创建者'; +comment on column sys_message.create_time is '创建时间'; +comment on column sys_message.update_by is '更新者'; +comment on column sys_message.update_time is '更新时间'; + + +-- ---------------------------- +-- 19、代码生成业务表 -- ---------------------------- create table if not exists gen_table ( @@ -977,7 +1020,7 @@ comment on column gen_table.update_time is '更新时间'; comment on column gen_table.remark is '备注'; -- ---------------------------- --- 19、代码生成业务表字段 +-- 20、代码生成业务表字段 -- ---------------------------- create table if not exists gen_table_column ( diff --git a/script/sql/ry_vue_5.X.sql b/script/sql/ry_vue_5.X.sql index 47bd63c1a..5ea96619a 100644 --- a/script/sql/ry_vue_5.X.sql +++ b/script/sql/ry_vue_5.X.sql @@ -689,7 +689,31 @@ insert into sys_notice values('2', '维护通知:2018-07-01 系统凌晨维护 -- ---------------------------- --- 18、代码生成业务表 +-- 18、消息记录表 +-- ---------------------------- +create table sys_message ( + message_id bigint(20) not null comment '消息ID', + category varchar(20) not null comment '消息分组(system/notice/workflow)', + type varchar(20) not null comment '消息类型', + source varchar(20) not null comment '消息来源', + title varchar(100) default '' comment '标题', + message varchar(500) default '' comment '摘要消息', + content longtext comment '详细内容', + data_json longtext comment '扩展数据JSON', + path varchar(500) default null comment '前端跳转路径', + send_user_ids varchar(2000) not null default '0' comment '目标用户ID串,0表示全局', + create_dept bigint(20) default null comment '创建部门', + create_by bigint(20) default null comment '创建者', + create_time datetime comment '创建时间', + update_by bigint(20) default null comment '更新者', + update_time datetime comment '更新时间', + primary key (message_id), + key idx_sys_message_category_time (category, create_time) +) engine=innodb comment = '消息记录表'; + + +-- ---------------------------- +-- 19、代码生成业务表 -- ---------------------------- create table gen_table ( table_id bigint(20) not null comment '编号', diff --git a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql index c9fb43761..aa4e9cf79 100644 --- a/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql +++ b/script/sql/sqlserver/sqlserver_ry_vue_5.X.sql @@ -1674,6 +1674,130 @@ GO INSERT sys_notice VALUES (2, N'维护通知:2018-07-01 若依系统凌晨维护', N'1', N'维护内容', N'0', 103, 1, getdate(), NULL, NULL, N'管理员') GO +CREATE TABLE sys_message +( + message_id bigint NOT NULL, + category nvarchar(20) NOT NULL, + type nvarchar(20) NOT NULL, + source nvarchar(20) NOT NULL, + title nvarchar(100) DEFAULT ('') NULL, + message nvarchar(500) DEFAULT ('') NULL, + content nvarchar(max) NULL, + data_json nvarchar(max) NULL, + path nvarchar(500) NULL, + send_user_ids nvarchar(2000) DEFAULT ('0') NOT NULL, + create_dept bigint NULL, + create_by bigint NULL, + create_time datetime2(7) NULL, + update_by bigint NULL, + update_time datetime2(7) NULL, + CONSTRAINT PK__sys_mess__0BBF6EE69F35486A PRIMARY KEY CLUSTERED (message_id) + WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) + ON [PRIMARY] +) +ON [PRIMARY] +TEXTIMAGE_ON [PRIMARY] +GO + +CREATE NONCLUSTERED INDEX idx_sys_message_category_time ON sys_message(category, create_time) +GO + +EXEC sys.sp_addextendedproperty + 'MS_Description', N'消息ID' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'message_id' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'消息分组(system/notice/workflow)' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'category' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'消息类型' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'type' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'消息来源' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'source' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'标题' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'title' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'摘要消息' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'message' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'详细内容' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'content' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'扩展数据JSON' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'data_json' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'前端跳转路径' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'path' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'目标用户ID串,0表示全局' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'send_user_ids' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建部门' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'create_dept' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'create_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'创建时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'create_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新者' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'update_by' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'更新时间' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message', + 'COLUMN', N'update_time' +GO +EXEC sys.sp_addextendedproperty + 'MS_Description', N'消息记录表' , + 'SCHEMA', N'dbo', + 'TABLE', N'sys_message' +GO + CREATE TABLE sys_oper_log ( oper_id bigint NOT NULL,