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

}
相关推荐
ggdpzhk16 分钟前
VUE:基于MVVN的前端js框架
前端·javascript·vue.js
活宝小娜5 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
世间万物皆对象5 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
程序视点5 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow5 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
刚刚好ā6 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
qq_17448285756 小时前
springboot基于微信小程序的旧衣回收系统的设计与实现
spring boot·后端·微信小程序
会发光的猪。8 小时前
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
前端·javascript·vue.js
天下代码客8 小时前
【vue】vue中.sync修饰符如何使用--详细代码对比
前端·javascript·vue.js
代码小鑫8 小时前
A043-基于Spring Boot的秒杀系统设计与实现
java·开发语言·数据库·spring boot·后端·spring·毕业设计