若依(RuoYi-Vue-Plus)框架使用WebSocket

框架:RuoYi-Vue-Plus

下载地址:

(1)后端代码 https://www.gitlink.org.cn/dromara-org/RuoYi-Vue-Plus/tree/5.X

https://gitee.com/dromara/RuoYi-Vue-Plus

https://gitee.com/dromara/RuoYi-Cloud-Plus

(2)前端代码 https://gitee.com/JavaLionLi/plus-ui

1.后台

1.1 开启websocket、sse

在 application.yml 中,找到 sse 及 websocket 的配置项,将 enabled 的值改为 true

1.2 业务模块引入 websocket 依赖

业务模块在 ruoyi-modules 下,如:ruoyi-xxx

XML 复制代码
<dependency>
	<groupId>org.dromara</groupId>
	<artifactId>ruoyi-common-websocket</artifactId>
</dependency>

ruoyi-common-websocket 为若依框架自带的公共组件,位于 ruoyi-common 下。

1.3 websocket 配置类

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;


@Configuration
@EnableWebSocket
public class HisWebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

1.4 websocket 消息接收、处理

java 复制代码
import com.alibaba.fastjson.JSON;
import jakarta.annotation.Resource;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.dromara.common.core.utils.SpringUtils;


@Slf4j
@Component
@ServerEndpoint("/websocket/message")
public class HisWebSocketServer {
    /** 自定义Service */
	@Resource
	private IWebsocketService websocketService;

	/**
	 * 连接建立成功调用的方法
	 */
	@OnOpen
	public void onOpen(Session session) {
		// log.info("Websocket连接建立成功");
	}

	@OnClose
	public void onClose(Session session) {
		// log.info("Websocket连接关闭");
	}

	/**
	 * 抛出异常时处理
	 */
	@OnError
	public void onError(Session session, Throwable exception) throws Exception {
		log.error("Websocket抛出异常时处理.", exception);
	}

	/**
	 * 服务器接收到客户端消息时调用的方法
	 */
	@OnMessage
	public void onMessage(String message, Session session) {
		log.info("Websocket接收到客户端消息.sessionId:{}, 消息:{}", session.getId(), message);

		if (StringUtils.isBlank(message)) {
			log.info("Websocket接收到的客户端消息为空,不做处理.sessionId:{}", session.getId());
			return;
		}
		try {
			// JSON格式
			String regex = "^(\\{.*\\}|\\[.*\\])$";

			if (!message.matches(regex)) {
				log.info("Websocket接收到的客户端消息不是JSON格式,不做处理.sessionId:{}, 消息:{}", session.getId(), message);
				return;
			}
            // 处理业务逻辑
            this.handleBizLogic(message, session);
			// session.getBasicRemote().sendText("服务器已收到消息");
		} catch (Exception e) {
			log.error("Websocket接收到客户端消息处理-异常:{}", e.getMessage(), e);
		}
	}

	/**
	 * 处理业务逻辑
	 *
	 * @date 2026-01-17
	 * @param message 客户端消息
	 * @param session 会话
	 * @since JDK 17
	 * @author 
	 */
	private void handleBizLogic(String message, Session session) throws Exception {
        // 客户端请求参数
        HisWebSocketReqVo reqVo = JSON.parseObject(message.trim(), HisWebSocketReqVo.class);
		
		if (websocketService == null) {
            websocketService = SpringUtils.getBean(WebsocketServiceImpl.class);
        }

        // TODO 具体业务处理
		
		// 发送消息到客户端(前端),可以是JSON格式,也可以是其他格式,和前端定好就行
		session.getBasicRemote().sendText(JSON.toJSONString(处理结果));
	}
}

在 @Component 引入 @Resource,websocketService=null,所以需要SpringUtils 重新实例化。

具体的业务处理,包括 调用service、dao(mapper)等。

到这儿,后台的配置就完成了。

2.前端

2.1 启用websocket、sse

全局搜索 VITE_APP_WEBSOCKET、VITE_APP_SSE,将其值改为 true

这样才能调用到后台服务。

2.2 引入websocket依赖

在使用到websocket的页面,引入依赖。

javascript 复制代码
import { initWebSocket } from '@/utils/websocket';

2.3 具体代码

其中的 ws://127.0.0.1:18080/websocket/message 即为后台的websocket 链接。

(1)127.0.0.1:18080 为后台服务的IP、端口号

(2)/websocket/message 为后台代码 HisWebSocketServer 定义的。

TypeScript 复制代码
// WebSocket连接
const baseClassListSocket = ref<any>(null);

// 初始化
onMounted(async () => {
  console.log('onMounted开始执行');

  // 创建WebSocket连接
  console.log('开始创建WebSocket连接');
  baseClassListSocket.value = createBaseClassListSocket();
});

// 组件销毁前重置isInitialized并关闭WebSocket连接
onBeforeUnmount(() => {

  // 关闭WebSocket连接
  if (baseClassListSocket.value) {
    baseClassListSocket.value.close();
    console.log('排班列表WebSocket连接已关闭');
  }
});

// 创建排班列表WebSocket连接
const createBaseClassListSocket = () => {
  // 使用后端提供的完整WebSocket地址
  const wsUrl = 'ws://127.0.0.1:18080/websocket/message';
  const connName = '排班列表WebSocket';
  console.log('创建排班列表WebSocket连接,URL:', wsUrl);
  return initWebSocket(wsUrl, {
    onConnected() {
      console.log(`${connName}已经连接`);
    },
    onDisconnected() {
      console.log(`${connName}已经断开`);
    },
    onMessage: (e) => {
      console.log(`${connName}收到原始消息:`, e.data);
      if (!e.data || e.data.indexOf('ping') > 0) {
        return;
      }
      try {
        const message = JSON.parse(e.data);
        console.log(`${connName}收到解析后消息:`, message);

        // 处理排班列表消息
        if (message.code === 200 && Array.isArray(message.data)) {
          handleBaseClassListMessage(message.data);
        }
      } catch (error) {
        console.error(`${connName}消息解析错误:`, error);
      }
    }
  });
};

// 处理排班列表WebSocket消息
const handleBaseClassListMessage = (data: any[]) => {
  // 合并结果,构建映射表
  const newMap: Record<string, any> = {};

  // 处理所有排班数据(包括医生和咨询师)
  data.forEach((userTypeGroup) => {
    if (userTypeGroup?.list) {
      userTypeGroup.list.forEach((user) => {
        if (user?.userId && user?.baseWorkClassList) {
          const userId = user.userId.toString();
          const duty = user.duty?.toString() || '';

          // 根据duty区分医生(1)和咨询师(11)
          if (duty === '1') {
            newMap[`doctor_${userId}`] = user.baseWorkClassList;
          } else if (duty === '11') {
            newMap[`consultant_${userId}`] = user.baseWorkClassList;
          }
        }
      });
    }
  });

  userBaseClassMap.value = newMap;
};

// 加载排班信息
const loadUserBaseClassList = async () => {
  try {
    isLoading.value = true;
    let startDateStr, endDateStr;
    if (boardViewMode.value === '日') {
      startDateStr = dayjs(dayInfoList.value[0].date).format('YYYY-MM-DD');
      endDateStr = startDateStr;
    } else {
      startDateStr = dayjs(dayInfoList.value[0].date).format('YYYY-MM-DD');
      endDateStr = dayjs(dayInfoList.value[6].date).format('YYYY-MM-DD');
    }

    // 通过WebSocket发送请求
    if (baseClassListSocket.value?.send) {
      const requestData = {
        startDate: startDateStr,
        endDate: endDateStr,
        duty: '' // 空字符串表示获取所有岗位
      };

      baseClassListSocket.value.send(JSON.stringify(requestData));
      console.log('排班列表WebSocket请求已发送:', requestData);
    } else {
      console.error('排班列表WebSocket连接未建立');
    }
  } catch (error) {
    console.error('加载排班信息失败', error);
  } finally {
    isLoading.value = false;
  }
};
相关推荐
Coder_Boy_1 小时前
基于SpringAI的在线考试系统-核心模块的数据模型交互关系
java·数据库·人工智能·spring boot·交互
yaoxin5211231 小时前
295. Java Stream API - 选择适用于并行计算的 BinaryOperator
java·开发语言
We....1 小时前
SpringBoot 微服务拦截器与负载均衡实践
java·spring boot·微服务·负载均衡
冬至喵喵1 小时前
RoaringBitmap与传统Bitmap
java·开发语言
better_liang2 小时前
Java技术栈中的MySQL数据结构应用与优化
java·数据结构·mysql·性能调优·索引优化
Swift社区2 小时前
Spring Boot 配置文件未生效
java·spring boot·后端
计算机程序设计小李同学2 小时前
基于Web和Android的漫画阅读平台
java·前端·vue.js·spring boot·后端·uniapp
沛沛老爹2 小时前
从Web到AI:Agent Skills CI/CD流水线集成实战指南
java·前端·人工智能·ci/cd·架构·llama·rag
ゞ 正在缓冲99%…2 小时前
2025.12.17华为软开
java·算法