提交
This commit is contained in:
parent
c429203106
commit
51a74d15be
@ -5,7 +5,7 @@
|
|||||||
"author": "Zheng Jie",
|
"author": "Zheng Jie",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vue-cli-service serve",
|
"dev": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve",
|
||||||
"build:prod": "vue-cli-service build",
|
"build:prod": "vue-cli-service build",
|
||||||
"build:stage": "vue-cli-service build --mode staging",
|
"build:stage": "vue-cli-service build --mode staging",
|
||||||
"preview": "node build/index.js --preview",
|
"preview": "node build/index.js --preview",
|
||||||
|
@ -1,33 +1,33 @@
|
|||||||
///*
|
/*
|
||||||
// * Copyright 2019-2025 Tz
|
* Copyright 2019-2025 Tz
|
||||||
// *
|
*
|
||||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// * you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
// * You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
// *
|
*
|
||||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
// *
|
*
|
||||||
// * Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// * See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
// * limitations under the License.
|
* limitations under the License.
|
||||||
// */
|
*/
|
||||||
//package me.zhengjie.config.webConfig;
|
package me.zhengjie.config.webConfig;
|
||||||
//
|
|
||||||
//import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
//import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
//import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
|
||||||
//
|
|
||||||
///**
|
/**
|
||||||
// * @author Tz
|
* @author Tz
|
||||||
// * @date 2019-08-24 15:44
|
* @date 2019-08-24 15:44
|
||||||
// */
|
*/
|
||||||
//@Configuration
|
@Configuration
|
||||||
//public class WebSocketConfig {
|
public class WebSocketConfig {
|
||||||
//
|
|
||||||
// @Bean
|
@Bean
|
||||||
// public ServerEndpointExporter serverEndpointExporter() {
|
public ServerEndpointExporter serverEndpointExporter() {
|
||||||
// return new ServerEndpointExporter();
|
return new ServerEndpointExporter();
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
|
@ -23,7 +23,10 @@ import org.springframework.boot.SpringApplication;
|
|||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.context.ApplicationPidFileWriter;
|
import org.springframework.boot.context.ApplicationPidFileWriter;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||||
|
import org.springframework.messaging.handler.annotation.SendTo;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
@ -92,7 +92,8 @@ public class SpringSecurityConfig {
|
|||||||
"/**/*.html",
|
"/**/*.html",
|
||||||
"/**/*.css",
|
"/**/*.css",
|
||||||
"/**/*.js",
|
"/**/*.js",
|
||||||
"/webSocket/**"
|
"/webSocket/**",
|
||||||
|
"/sdcp/**"
|
||||||
).permitAll()
|
).permitAll()
|
||||||
// swagger 文档
|
// swagger 文档
|
||||||
.antMatchers("/swagger-ui.html").permitAll()
|
.antMatchers("/swagger-ui.html").permitAll()
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
package me.zhengjie.modules.security.config;
|
|
||||||
|
|
||||||
import me.zhengjie.modules.security.config.handler.DeviceWebSocketHandler;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
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.config.annotation.WebSocketConfigurer;
|
|
||||||
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
|
|
||||||
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* WebSocket配置类,启用WebSocket功能并注册处理器
|
|
||||||
*/
|
|
||||||
@Configuration
|
|
||||||
@EnableWebSocket
|
|
||||||
public class WebSocketConfig implements WebSocketConfigurer {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private DeviceWebSocketHandler deviceWebSocketHandler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 注册WebSocket处理器,配置端点和允许的源
|
|
||||||
* @param registry WebSocket处理器注册器
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
|
||||||
registry.addHandler(deviceWebSocketHandler, "/ws/device")
|
|
||||||
.setAllowedOrigins("*");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 配置WebSocket容器参数,设置消息缓冲区大小
|
|
||||||
* @return WebSocket容器工厂Bean
|
|
||||||
*/
|
|
||||||
@Bean
|
|
||||||
public ServletServerContainerFactoryBean createWebSocketContainer() {
|
|
||||||
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
|
|
||||||
container.setMaxTextMessageBufferSize(8192);
|
|
||||||
container.setMaxBinaryMessageBufferSize(8192);
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2025 Tz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package me.zhengjie.modules.security.config.enums;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ZhangHouYing
|
||||||
|
* @date 2019-08-10 9:56
|
||||||
|
*/
|
||||||
|
public enum MsgType {
|
||||||
|
/** 连接 */
|
||||||
|
CONNECT,
|
||||||
|
/** 关闭 */
|
||||||
|
CLOSE,
|
||||||
|
/** 信息 */
|
||||||
|
INFO,
|
||||||
|
/** 错误 */
|
||||||
|
ERROR
|
||||||
|
}
|
@ -1,77 +0,0 @@
|
|||||||
package me.zhengjie.modules.security.config.handler;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.socket.CloseStatus;
|
|
||||||
import org.springframework.web.socket.TextMessage;
|
|
||||||
import org.springframework.web.socket.WebSocketSession;
|
|
||||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理客户端WebSocket连接的处理器
|
|
||||||
* 管理客户端会话并转发设备状态变化
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class DeviceWebSocketHandler extends TextWebSocketHandler {
|
|
||||||
|
|
||||||
@Autowired @Lazy
|
|
||||||
private ThirdPartyWebSocketClient thirdPartyClient;
|
|
||||||
|
|
||||||
// 存储所有活跃的WebSocket会话,键为会话ID
|
|
||||||
private final ConcurrentHashMap<String, WebSocketSession> sessions = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理来自客户端的文本消息
|
|
||||||
* @param session 客户端会话
|
|
||||||
* @param message 收到的消息
|
|
||||||
* @throws IOException 发送消息失败时抛出
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
|
|
||||||
String payload = message.getPayload();
|
|
||||||
// 将客户端指令转发给第三方WebSocket服务
|
|
||||||
thirdPartyClient.sendCommand(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端连接建立后的回调方法
|
|
||||||
* @param session 新建立的会话
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void afterConnectionEstablished(WebSocketSession session) {
|
|
||||||
sessions.put(session.getId(), session);
|
|
||||||
// 可在此处添加连接初始化逻辑
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 客户端连接关闭后的回调方法
|
|
||||||
* @param session 关闭的会话
|
|
||||||
* @param status 关闭状态
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
|
|
||||||
sessions.remove(session.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通知所有客户端设备状态变化
|
|
||||||
* @param deviceId 设备ID
|
|
||||||
* @param status 设备状态
|
|
||||||
*/
|
|
||||||
public void notifyDeviceChange(String deviceId, String status) {
|
|
||||||
sessions.forEach((id, session) -> {
|
|
||||||
try {
|
|
||||||
// 向客户端发送JSON格式的设备状态消息
|
|
||||||
session.sendMessage(new TextMessage(
|
|
||||||
String.format("{\"deviceId\": \"%s\", \"status\": \"%s\"}", deviceId, status)
|
|
||||||
));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,148 +0,0 @@
|
|||||||
package me.zhengjie.modules.security.config.handler;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.context.annotation.Lazy;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
import org.springframework.web.socket.client.WebSocketClient;
|
|
||||||
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
|
|
||||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
|
||||||
import org.springframework.web.socket.WebSocketSession;
|
|
||||||
import org.springframework.web.socket.TextMessage;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 与第三方WebSocket服务通信的客户端
|
|
||||||
* 负责建立连接、订阅设备和处理消息
|
|
||||||
*/
|
|
||||||
@Component
|
|
||||||
public class ThirdPartyWebSocketClient extends TextWebSocketHandler {
|
|
||||||
|
|
||||||
@Value("${thirdparty.ws.url}")
|
|
||||||
private String thirdPartyWsUrl;
|
|
||||||
|
|
||||||
@Autowired @Lazy
|
|
||||||
private DeviceWebSocketHandler deviceHandler;
|
|
||||||
|
|
||||||
private WebSocketSession session;
|
|
||||||
// 存储已订阅的设备,键为设备ID,值为订阅状态
|
|
||||||
private final ConcurrentHashMap<String, String> subscribedDevices = new ConcurrentHashMap<>();
|
|
||||||
private final ScheduledExecutorService reconnectExecutor = Executors.newSingleThreadScheduledExecutor();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bean初始化后连接到第三方WebSocket服务
|
|
||||||
*/
|
|
||||||
@PostConstruct
|
|
||||||
public void init() {
|
|
||||||
// connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 建立与第三方WebSocket服务的连接
|
|
||||||
*/
|
|
||||||
private void connect() {
|
|
||||||
WebSocketClient client = new StandardWebSocketClient();
|
|
||||||
try {
|
|
||||||
session = client.doHandshake(this, thirdPartyWsUrl).get();
|
|
||||||
System.out.println("Connected to third-party WebSocket server");
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
// 连接失败,安排重连
|
|
||||||
scheduleReconnect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 安排重连任务
|
|
||||||
*/
|
|
||||||
private void scheduleReconnect() {
|
|
||||||
reconnectExecutor.schedule(this::connect, 5, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 订阅指定设备的状态变化
|
|
||||||
* @param deviceId 设备ID
|
|
||||||
*/
|
|
||||||
public void subscribeDevice(String deviceId) {
|
|
||||||
if (session != null && session.isOpen()) {
|
|
||||||
try {
|
|
||||||
// 发送订阅消息到第三方服务
|
|
||||||
session.sendMessage(new TextMessage(
|
|
||||||
String.format("{\"action\": \"subscribe\", \"deviceId\": \"%s\"}", deviceId)
|
|
||||||
));
|
|
||||||
subscribedDevices.put(deviceId, "subscribed");
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 向第三方服务发送指令
|
|
||||||
* @param command 指令内容
|
|
||||||
*/
|
|
||||||
public void sendCommand(String command) {
|
|
||||||
if (session != null && session.isOpen()) {
|
|
||||||
try {
|
|
||||||
session.sendMessage(new TextMessage(command));
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理从第三方服务接收到的文本消息
|
|
||||||
* @param session 会话
|
|
||||||
* @param message 收到的消息
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
|
|
||||||
String payload = message.getPayload();
|
|
||||||
// 处理设备状态变化消息
|
|
||||||
processDeviceStatusChange(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析设备状态变化消息并通知客户端
|
|
||||||
* @param payload 消息内容
|
|
||||||
*/
|
|
||||||
private void processDeviceStatusChange(String payload) {
|
|
||||||
// 解析消息并提取设备ID和状态
|
|
||||||
String deviceId = extractDeviceId(payload);
|
|
||||||
String status = extractStatus(payload);
|
|
||||||
|
|
||||||
if (deviceId != null && status != null) {
|
|
||||||
// 通知所有连接的客户端设备状态变化
|
|
||||||
deviceHandler.notifyDeviceChange(deviceId, status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从消息中提取设备ID
|
|
||||||
* 需要根据第三方服务的消息格式实现具体解析逻辑
|
|
||||||
* @param payload 消息内容
|
|
||||||
* @return 设备ID
|
|
||||||
*/
|
|
||||||
private String extractDeviceId(String payload) {
|
|
||||||
// TODO: 实现JSON解析逻辑
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从消息中提取设备状态
|
|
||||||
* 需要根据第三方服务的消息格式实现具体解析逻辑
|
|
||||||
* @param payload 消息内容
|
|
||||||
* @return 设备状态
|
|
||||||
*/
|
|
||||||
private String extractStatus(String payload) {
|
|
||||||
// TODO: 实现JSON解析逻辑
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +1,36 @@
|
|||||||
package me.zhengjie.modules.system.rest;
|
package me.zhengjie.modules.system.rest;
|
||||||
|
|
||||||
import me.zhengjie.modules.system.service.socket.DeviceService;
|
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.messaging.handler.annotation.SendTo;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.stereotype.Controller;
|
||||||
|
|
||||||
@RestController
|
@Controller
|
||||||
@RequestMapping("/api/printer")
|
|
||||||
public class BusDeviceSocketController {
|
public class BusDeviceSocketController {
|
||||||
|
|
||||||
@Autowired DeviceService deviceService;
|
// 处理客户端发送到/app/send的消息
|
||||||
|
@MessageMapping("/hello") // 接收路径:/app/send
|
||||||
/**
|
@SendTo("/topic/greetings") // 响应路径:/topic/messages
|
||||||
* 订阅设备状态变化的HTTP接口
|
public String handleMessage(String message) {
|
||||||
* @param deviceId 设备ID
|
System.out.println("Received message: " + message);
|
||||||
*/
|
return "Message received: " + message;
|
||||||
@PostMapping("/{deviceId}/subscribe")
|
|
||||||
public void subscribe(@PathVariable String deviceId) {
|
|
||||||
deviceService.subscribeToDevice(deviceId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// /**
|
||||||
* 向设备发送指令的HTTP接口
|
// * 订阅设备状态变化的HTTP接口
|
||||||
* @param deviceId 设备ID
|
// * @param deviceId 设备ID
|
||||||
* @param command 指令内容
|
// */
|
||||||
*/
|
// @PostMapping("/{deviceId}/subscribe")
|
||||||
@PostMapping("/{deviceId}/command")
|
// public void subscribe(@PathVariable String deviceId) {
|
||||||
public void sendCommand(@PathVariable String deviceId, @RequestBody String command) {
|
// deviceService.subscribeToDevice(deviceId);
|
||||||
deviceService.sendDeviceCommand(deviceId, command);
|
// }
|
||||||
}
|
//
|
||||||
|
// /**
|
||||||
|
// * 向设备发送指令的HTTP接口
|
||||||
|
// * @param deviceId 设备ID
|
||||||
|
// * @param command 指令内容
|
||||||
|
// */
|
||||||
|
// @PostMapping("/{deviceId}/command")
|
||||||
|
// public void sendCommand(@PathVariable String deviceId, @RequestBody String command) {
|
||||||
|
// deviceService.sendDeviceCommand(deviceId, command);
|
||||||
|
// }
|
||||||
}
|
}
|
@ -1,37 +0,0 @@
|
|||||||
package me.zhengjie.modules.system.service.socket;
|
|
||||||
|
|
||||||
import me.zhengjie.modules.security.config.handler.ThirdPartyWebSocketClient;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 设备管理服务,提供与设备相关的业务逻辑
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class DeviceService {
|
|
||||||
|
|
||||||
private final ThirdPartyWebSocketClient webSocketClient;
|
|
||||||
|
|
||||||
public DeviceService(ThirdPartyWebSocketClient webSocketClient) {
|
|
||||||
this.webSocketClient = webSocketClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 订阅设备状态变化
|
|
||||||
* @param deviceId 设备ID
|
|
||||||
*/
|
|
||||||
public void subscribeToDevice(String deviceId) {
|
|
||||||
webSocketClient.subscribeDevice(deviceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 向设备发送指令
|
|
||||||
* @param deviceId 设备ID
|
|
||||||
* @param command 指令内容
|
|
||||||
*/
|
|
||||||
public void sendDeviceCommand(String deviceId, String command) {
|
|
||||||
// 构建符合第三方服务格式的指令
|
|
||||||
String formattedCommand = String.format("{\"deviceId\": \"%s\", \"command\": \"%s\"}",
|
|
||||||
deviceId, command);
|
|
||||||
webSocketClient.sendCommand(formattedCommand);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2025 Tz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package me.zhengjie.modules.system.service.webstocket;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import me.zhengjie.modules.security.config.enums.MsgType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ZhangHouYing
|
||||||
|
* @date 2019-08-10 9:55
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class SocketMsg {
|
||||||
|
private String msg;
|
||||||
|
private MsgType msgType;
|
||||||
|
|
||||||
|
public SocketMsg(String msg, MsgType msgType) {
|
||||||
|
this.msg = msg;
|
||||||
|
this.msgType = msgType;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2025 Tz
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package me.zhengjie.modules.system.service.webstocket;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.websocket.*;
|
||||||
|
import javax.websocket.server.PathParam;
|
||||||
|
import javax.websocket.server.ServerEndpoint;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
|
@ServerEndpoint("/sdcp/{method}/{mainboardId}")
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class WebSocketServer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
|
||||||
|
*/
|
||||||
|
private static final CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
|
||||||
|
*/
|
||||||
|
private Session session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接收sid
|
||||||
|
*/
|
||||||
|
private String sid = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接建立成功调用的方法
|
||||||
|
*/
|
||||||
|
@OnOpen
|
||||||
|
public void onOpen(Session session, @PathParam("method") String method, @PathParam("mainboardId") String mainboardId) {
|
||||||
|
this.session = session;
|
||||||
|
//如果存在就先删除一个,防止重复推送消息
|
||||||
|
webSocketSet.removeIf(webSocket -> webSocket.sid.equals(mainboardId));
|
||||||
|
webSocketSet.add(this);
|
||||||
|
this.sid = mainboardId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 连接关闭调用的方法
|
||||||
|
*/
|
||||||
|
@OnClose
|
||||||
|
public void onClose() {
|
||||||
|
webSocketSet.remove(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 收到客户端消息后调用的方法
|
||||||
|
*
|
||||||
|
* @param message 客户端发送过来的消息
|
||||||
|
*/
|
||||||
|
@OnMessage
|
||||||
|
public void onMessage(String message, Session session, @PathParam("method") String method, @PathParam("mainboardId") String mainboardId) {
|
||||||
|
log.info("收到来" + sid + "的信息:" + message);
|
||||||
|
if ("ping".equals(message)) {
|
||||||
|
try {
|
||||||
|
sendMessage("pong");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 群发消息
|
||||||
|
for (WebSocketServer item : webSocketSet) {
|
||||||
|
try {
|
||||||
|
item.sendMessage(message);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnError
|
||||||
|
public void onError(Session session, Throwable error) {
|
||||||
|
log.error("发生错误", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实现服务器主动推送
|
||||||
|
*/
|
||||||
|
private void sendMessage(String message) throws IOException {
|
||||||
|
this.session.getBasicRemote().sendText(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 群发自定义消息
|
||||||
|
*/
|
||||||
|
public static void sendInfo(SocketMsg socketMsg, @PathParam("mainboardId") String mainboardId) throws IOException {
|
||||||
|
String message = JSON.toJSONString(socketMsg);
|
||||||
|
log.info("推送消息到" + mainboardId + ",推送内容:" + message);
|
||||||
|
for (WebSocketServer item : webSocketSet) {
|
||||||
|
try {
|
||||||
|
//这里可以设定只推送给这个sid的,为null则全部推送
|
||||||
|
if (mainboardId == null) {
|
||||||
|
item.sendMessage(message);
|
||||||
|
} else if (item.sid.equals(mainboardId)) {
|
||||||
|
item.sendMessage(message);
|
||||||
|
}
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
WebSocketServer that = (WebSocketServer) o;
|
||||||
|
return Objects.equals(session, that.session) &&
|
||||||
|
Objects.equals(sid, that.sid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(session, sid);
|
||||||
|
}
|
||||||
|
}
|
@ -107,10 +107,16 @@ weChat:
|
|||||||
appId: wx583d0c321ef43dfe
|
appId: wx583d0c321ef43dfe
|
||||||
secret: 1b5eca11a1058361b0f14c5933ef6bd6
|
secret: 1b5eca11a1058361b0f14c5933ef6bd6
|
||||||
|
|
||||||
# socket地址
|
# socket
|
||||||
thirdparty:
|
|
||||||
ws:
|
ws:
|
||||||
url: ws://127.0.0.1:3030/websocket
|
url: ws://127.0.0.1:8000/webSocket
|
||||||
|
server:
|
||||||
|
# 客户端心跳开关
|
||||||
|
clientSwitch: false
|
||||||
|
# 间隔时间 单位:秒
|
||||||
|
interval: 10
|
||||||
|
# 最大错误次数
|
||||||
|
maxMissCount: 10
|
||||||
|
|
||||||
# 文件存储路径
|
# 文件存储路径
|
||||||
file:
|
file:
|
||||||
|
@ -91,6 +91,12 @@
|
|||||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.webjars</groupId>
|
||||||
|
<artifactId>sockjs-client</artifactId>
|
||||||
|
<version>1.0.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- mybatis -->
|
<!-- mybatis -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.baomidou</groupId>
|
<groupId>com.baomidou</groupId>
|
||||||
|
Loading…
Reference in New Issue
Block a user