基于websocket简易封装一个全局消息通知组件

期望是,在用户登录后台后。新的任务到来时能够及时通知到并且去处理。

效果图

前端

html 复制代码
<template>
  <div></div>
</template>
  
<script>
import { updateRead } from "@/api/system/approval-notification";
export default {
  data() {
    return {};
  },
  mounted() {},

  methods: {
    // 初始化方法
    init(userId) {
      // 1、websocket接口的url,使用传入的id参数
      let ws = `http://localhost:8080/ws/platformAsync/${userId}`;

      // 实例化socket
      this.socket = new WebSocket(ws);
      // 监听socket连接
      this.socket.onopen = this.socketopen;
      // 监听socket错误信息
      this.socket.onerror = this.error;
      // 监听socket消息
      this.socket.onmessage = this.getMessage;
      // 监听socket断开连接的消息
      this.socket.onclose = this.close; // 修正为 onclose
    },

    // 连接成功方法
    socketopen() {
      console.log("socket连接成功");
    },
    // 连接错误
    error() {
      console.log("连接错误");
    },
    // 接受信息接口
    getMessage(message) {
      // 当接受到信息之后,就可以做后续的处理了
      let data = JSON.parse(message.data);

      const h = this.$createElement;
      let notificationInstance = this.$notify.info({
        title: data.title,
        message: h("div", [
          h("div", data.message),
          h(
            "el-button", // 使用 Element UI 的按钮组件
            {
              props: {
                type: "text", // 设置按钮类型为文字按钮
                siez: "mini",
              },
              on: {
                click: () => {
                  this.handleAction(data.path);
                  notificationInstance.close(); // 关闭通知
                },
              },
            },
            "前往处理"
          ),
          h(
            "el-button",
            {
              props: {
                type: "text",
                siez: "mini",
              },
              on: {
                click: () => {
                  this.read(data);
                  notificationInstance.close();
                },
              },
            },
            "已读"
          ),
        ]),
        duration: 10 * 1000,
        position: "bottom-right",
      });
    },
    // 关闭处理
    close() {
      console.log("连接关闭");
    },
    handleAction(path) {
      this.$router.push(path);
    },
    read(data) {
      updateRead(data.id);
    },
  },
};
</script>

后端

java 复制代码
@Slf4j
@Component
@ServerEndpoint(value = "/ws/platformAsync/{userId}")
public class PlatformAsyncWebSocket {

    // 用来存储每一个客户端对象对应的WsController对象
    private static Map<String, PlatformAsyncWebSocket> onlineUsers = new ConcurrentHashMap<>();

    // 声明Session对象,通过该对象可以给指定的用户发送请求
    private Session session;

    /**
     * 连接建立时被调用
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        log.info("连接成功");
        // 将局部的session对象赋值给成员session对象
        this.session = session;
        // 这里是因为前端在传数据的时候,会将userId传过来
        // 所以使用将userId和websocket对象存储起来,方便下次服务端推送信息的时候使用
        Map<String, List<String>> requestParameterMap = this.session.getRequestParameterMap();
        List<String> userIds = requestParameterMap.get("userId");
        String userId = userIds.get(0);
        onlineUsers.put(userId, this);
    }

    /**
     * 接收到客户端消息时被调用
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        // 处理接收到的消息
    }

    /**
     * 连接被关闭时调用
     */
    @OnClose
    public void onClose(Session session) {
        // 关闭时则将map中的用户移除
        Map<String, List<String>> requestParameterMap = session.getRequestParameterMap();
        List<String> userIds = requestParameterMap.get("userId");
        String userId = userIds.get(0);
        onlineUsers.remove(userId);
    }

    /**
     * 推送消息,将消息推送给某个指定的用户
     */
    public void sendMsg(String userId, String message) {
        try {
            PlatformAsyncWebSocket wsController = onlineUsers.get(userId);
            if (wsController != null && wsController.session.isOpen()) {
                wsController.session.getBasicRemote().sendText(message);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

创建一个定时器做测试

java 复制代码
@Slf4j
@Component
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class ScheduledTask {

    private final PlatformAsyncWebSocket platformAsyncWebSocket;

    private final ApprovalNotificationService approvalNotificationService;

    @Scheduled(fixedRate = 60 * 1000) // 每x秒执行一次
    public void sendQualityCheckNotifications() {
        List<ApprovalNotificationDto> approvalNotificationDtoList = approvalNotificationService.getApprovalNotificationDtoList();
        for (ApprovalNotificationDto approvalNotificationDto : approvalNotificationDtoList) {
            PlatformInfo platformInfo = new PlatformInfo();
            String userId = approvalNotificationDto.getRecipientId();
            platformInfo.setId(String.valueOf(approvalNotificationDto.getId()));
            platformInfo.setTitle(approvalNotificationDto.getTitle());
            platformInfo.setMessage(approvalNotificationDto.getContent()); // 自定义消息内容
            platformAsyncWebSocket.sendMsg(userId, JSON.toJSONString(platformInfo));
        }
    }

}
相关推荐
一勺菠萝丶18 分钟前
Spring Boot + MyBatis/MyBatis Plus:XML中循环处理List参数的终极指南
xml·spring boot·mybatis
RainbowSea2 小时前
问题:后端由于字符内容过长,前端展示精度丢失修复
java·spring boot·后端
风象南2 小时前
SpringBoot 控制器的动态注册与卸载
java·spring boot·后端
markyankee1012 小时前
Vue.js 入门指南:从零开始构建你的第一个应用
vue.js
我是一只代码狗2 小时前
springboot中使用线程池
java·spring boot·后端
hello早上好3 小时前
JDK 代理原理
java·spring boot·spring
PanZonghui3 小时前
Centos项目部署之运行SpringBoot打包后的jar文件
linux·spring boot
khalil3 小时前
基于 Vue3实现一款简历生成工具
前端·vue.js
沉着的码农3 小时前
【设计模式】基于责任链模式的参数校验
java·spring boot·分布式
zyxzyx6663 小时前
Flyway 介绍以及与 Spring Boot 集成指南
spring boot·笔记