From e5347567d7c06e7572a455c3ed0e6617c74a0718 Mon Sep 17 00:00:00 2001 From: syruan <15555146157@163.com> Date: Wed, 24 Dec 2025 09:32:57 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=20CORS=20=E8=B7=A8=E5=9F=9F?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=92=8C=20Socket=20=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=99=A8=EF=BC=8C=E6=94=AF=E6=8C=81=20JTT808?= =?UTF-8?q?=20=E8=AE=BE=E5=A4=87=E7=9A=84=E8=BF=9E=E6=8E=A5=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E4=B8=8E=E6=8C=87=E4=BB=A4=E4=B8=8B=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sgzb/material/config/CorsConfig.java | 30 +++ .../utils/SocketConnectionManager.java | 178 ++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/config/CorsConfig.java create mode 100644 sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/utils/SocketConnectionManager.java diff --git a/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/config/CorsConfig.java b/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/config/CorsConfig.java new file mode 100644 index 0000000..1fa0f56 --- /dev/null +++ b/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/config/CorsConfig.java @@ -0,0 +1,30 @@ +package com.bonus.sgzb.material.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * CORS跨域配置 + * + * @author system + */ +@Configuration +public class CorsConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + // 允许所有来源(开发环境) + .allowedOriginPatterns("*") + // 允许的请求方法 + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH") + // 允许的请求头 + .allowedHeaders("*") + // 允许携带凭证 + .allowCredentials(true) + // 预检请求的有效期(秒) + .maxAge(3600); + } +} + diff --git a/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/utils/SocketConnectionManager.java b/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/utils/SocketConnectionManager.java new file mode 100644 index 0000000..58cf5ed --- /dev/null +++ b/sgzb-modules/sgzb-material/src/main/java/com/bonus/sgzb/material/utils/SocketConnectionManager.java @@ -0,0 +1,178 @@ +package com.bonus.sgzb.material.utils; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Socket 连接管理器 + * 用于管理 JTT808 设备的 Socket 连接,支持主动下发指令 + * + * @author system + * @date 2025-12-23 + */ +@Slf4j +@Component +public class SocketConnectionManager { + + /** + * 终端手机号 -> Socket 连接的映射 + */ + private final Map connectionMap = new ConcurrentHashMap<>(); + + /** + * 添加连接 + * + * @param phoneNumber 终端手机号 + * @param socket Socket 连接 + * @return 是否添加成功 + */ + public boolean addConnection(String phoneNumber, Socket socket) { + if (phoneNumber == null || socket == null) { + log.warn("添加连接失败: phoneNumber 或 socket 为 null"); + return false; + } + + // 如果已存在连接,关闭旧连接 + Socket oldSocket = connectionMap.get(phoneNumber); + if (oldSocket != null && !oldSocket.isClosed()) { + log.info("终端 {} 已有连接,关闭旧连接", phoneNumber); + try { + oldSocket.close(); + } catch (IOException e) { + log.error("关闭旧连接异常: {}", e.getMessage()); + } + } + + connectionMap.put(phoneNumber, socket); + log.info("添加连接: {} -> {}", phoneNumber, socket.getRemoteSocketAddress()); + return true; + } + + /** + * 移除连接 + * + * @param phoneNumber 终端手机号 + * @return 是否移除成功 + */ + public boolean removeConnection(String phoneNumber) { + if (phoneNumber == null) { + return false; + } + + Socket socket = connectionMap.remove(phoneNumber); + if (socket != null) { + log.info("移除连接: {}", phoneNumber); + return true; + } + return false; + } + + /** + * 获取连接 + * + * @param phoneNumber 终端手机号 + * @return Socket 连接,如果不存在或已关闭则返回 null + */ + public Socket getConnection(String phoneNumber) { + if (phoneNumber == null) { + return null; + } + + Socket socket = connectionMap.get(phoneNumber); + if (socket != null && socket.isClosed()) { + // 连接已关闭,从映射中移除 + connectionMap.remove(phoneNumber); + return null; + } + return socket; + } + + /** + * 检查终端是否在线(有活跃的 Socket 连接) + * + * @param phoneNumber 终端手机号 + * @return 是否在线 + */ + public boolean isOnline(String phoneNumber) { + Socket socket = getConnection(phoneNumber); + return socket != null && socket.isConnected() && !socket.isClosed(); + } + + /** + * 向终端发送数据 + * + * @param phoneNumber 终端手机号 + * @param data 要发送的数据 + * @return 是否发送成功 + */ + public boolean sendData(String phoneNumber, byte[] data) { + if (phoneNumber == null || data == null || data.length == 0) { + log.warn("发送数据失败: phoneNumber 或 data 为空"); + return false; + } + + Socket socket = getConnection(phoneNumber); + if (socket == null) { + log.warn("发送数据失败: 终端 {} 不在线", phoneNumber); + return false; + } + + try { + OutputStream outputStream = socket.getOutputStream(); + outputStream.write(data); + outputStream.flush(); + log.info("向终端 {} 发送数据成功: {} 字节", phoneNumber, data.length); + return true; + } catch (IOException e) { + log.error("向终端 {} 发送数据失败: {}", phoneNumber, e.getMessage()); + // 发送失败,移除连接 + removeConnection(phoneNumber); + return false; + } + } + + /** + * 获取当前在线终端数量 + * + * @return 在线终端数量 + */ + public int getOnlineCount() { + // 清理已关闭的连接 + connectionMap.entrySet().removeIf(entry -> entry.getValue().isClosed()); + return connectionMap.size(); + } + + /** + * 根据 Socket 查找终端手机号 + * + * @param socket Socket 连接 + * @return 终端手机号,如果未找到则返回 null + */ + public String getPhoneNumberBySocket(Socket socket) { + if (socket == null) { + return null; + } + + for (Map.Entry entry : connectionMap.entrySet()) { + if (entry.getValue() == socket) { + return entry.getKey(); + } + } + return null; + } + + /** + * 清空所有连接 + */ + public void clearAll() { + connectionMap.clear(); + log.info("清空所有连接"); + } +} +