基于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));
        }
    }

}
相关推荐
cuiyaonan200037 分钟前
SpringBoot 下的Excel文件损坏与内容乱码问题
spring boot·后端·excel
x原力觉醒1 小时前
uniapp跨域问题,在开发环境中配置
javascript·vue.js·uni-app
王哲晓1 小时前
第十五章 Vue工程化开发及Vue CLI脚手架
前端·javascript·vue.js
鱼跃鹰飞2 小时前
大厂面试真题-简单描述一下SpringBoot的启动过程
java·spring boot·后端·spring·面试
大只因bug2 小时前
基于Springboot的在线考试与学习交流平台的设计与实现
java·spring boot·后端·学习·mysql·vue·在线考试与学习交流平台系统
爱吃奶酪的松鼠丶2 小时前
net mvc中使用vue自定义组件遇到的坑
前端·vue.js·mvc
想进大厂的小王2 小时前
Spring Boot⾃动配置
java·spring boot·后端
小威编程2 小时前
uni-app应用级生命周期和页面级生命周期
前端·vue.js·uni-app
Ylucius3 小时前
14天速成前端 ------学习日志(已完结)------ 后端程序员学习了解前端
java·开发语言·前端·vue.js·学习·状态模式·1024程序员节