From 96255ab9e6daf27030e2dc1bee909cd1af8f2e5e Mon Sep 17 00:00:00 2001 From: tangw Date: Sun, 25 Jan 2026 22:23:57 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B4=A8=E6=8E=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../websocket/ChatWebSocketServer.java | 67 ++++++++++++++-- .../websocket/WebSocketInstrComm.java | 2 +- .../websocket/WebSocketMsgServiceImpl.java | 79 ++++++++++++++++++- .../websocket/WebSocketUser.java | 1 + .../zycx/service/impl/ZycxServiceImpl.java | 1 + 5 files changed, 140 insertions(+), 10 deletions(-) diff --git a/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/ChatWebSocketServer.java b/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/ChatWebSocketServer.java index a7cad74..32be492 100644 --- a/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/ChatWebSocketServer.java +++ b/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/ChatWebSocketServer.java @@ -33,6 +33,7 @@ public class ChatWebSocketServer { private static final Set CORE_PARAMS = new HashSet<>(Arrays.asList("token", "username", "role")); // 🌟 容器改为存储封装后的WebSocketUser(key=userId) private static final Map ONLINE_USER_MAP = new ConcurrentHashMap<>(); + private static final Map IP_TO_USERS_MAP = new ConcurrentHashMap<>(); /** * 连接建立时:封装用户信息并存入容器 * 注意:若需要传递username/role等属性,可通过路径参数/请求参数/Token解析 @@ -79,6 +80,9 @@ public class ChatWebSocketServer { } // 5. 存入全局容器 ONLINE_USER_MAP.put(userId, webSocketUser); + if(loginIp!=null&&!"".equals(loginIp)&&userId!=null&&!"".equals(userId)&&!"未知IP".equals(loginIp)){ + updateIpIndex(loginIp,userId); + } System.out.println("用户[" + userId + "-" + username + "]连接成功,IP:" + loginIp + ",当前在线人数:" + ONLINE_USER_MAP.size()); } // ========== 消息接收核心方法 ========== @@ -166,6 +170,7 @@ public class ChatWebSocketServer { @OnClose public void onClose(@PathParam("userId") String userId, Session session) { ONLINE_USER_MAP.remove(userId); + ChatWebSocketServer.removeUserAllIpIndex(userId); System.out.println("用户[" + userId + "]断开连接,当前在线人数:" + ONLINE_USER_MAP.size()); } @@ -176,6 +181,7 @@ public class ChatWebSocketServer { public void onError(@PathParam("userId") String userId, Session session, Throwable throwable) { throwable.printStackTrace(); ONLINE_USER_MAP.remove(userId); + ChatWebSocketServer.removeUserAllIpIndex(userId); } // ========== 工具方法:获取客户端IP ========== @@ -188,7 +194,38 @@ public class ChatWebSocketServer { return "未知IP"; } } + // 1. 用户上线/修改IP时,更新索引(新增/覆盖) + public static void updateIpIndex(String loginIp, String userId) { + if (loginIp == null || loginIp.trim().isEmpty() || userId == null) { + return; + } + IP_TO_USERS_MAP.put(loginIp.trim(), userId); // 唯一IP直接覆盖,保证最新 + } + // 2. 用户下线/IP失效时,删除索引 + public static void removeIpIndex(String loginIp) { + if (loginIp == null || loginIp.trim().isEmpty()) { + return; + } + IP_TO_USERS_MAP.remove(loginIp.trim()); + } + // ========== 3. 新增:批量删除某用户的所有IP索引(核心,供外部调用) ========== + public static void removeUserAllIpIndex(String userId) { + if (userId == null || userId.trim().isEmpty()) { + return; + } + // 步骤1:收集该用户对应的所有IP + List userIps = new ArrayList<>(); + for (Map.Entry entry : IP_TO_USERS_MAP.entrySet()) { + if (userId.equals(entry.getValue())) { + userIps.add(entry.getKey()); + } + } + // 步骤2:批量删除这些IP的索引 + for (String ip : userIps) { + IP_TO_USERS_MAP.remove(ip); + } + } // ========== 🌟 暴露属性查询方法(业务层调用) ========== /** @@ -218,6 +255,15 @@ public class ChatWebSocketServer { } return roleUsers; } + /** + * 新增:通过IP快速查询对应的userId(核心效率方法) + */ + public static String getUserIdByIp(String ip) { + if (ip == null || ip.trim().isEmpty()) { + return null; + } + return IP_TO_USERS_MAP.get(ip.trim()); + } /** * 根据登录IP筛选在线用户(精确匹配) * @param loginIp 要查询的登录IP(如 "192.168.1.100") @@ -225,17 +271,24 @@ public class ChatWebSocketServer { */ public static List getOnlineUsersByLoginIp(String loginIp) { List ipUsers = new ArrayList<>(); - // 前置空值校验:避免传入null/空字符串导致无效查询 + // 前置空值校验 if (loginIp == null || loginIp.trim().isEmpty()) { return ipUsers; } - // 遍历在线用户,筛选匹配IP且连接有效的用户 - for (WebSocketUser user : ONLINE_USER_MAP.values()) { - // 双重校验:IP精确匹配 + 连接有效 - if (loginIp.equals(user.getLoginIp()) && user.isConnected()) { - ipUsers.add(user); - } + + // 核心改造:直接从索引Map拿userId,无需遍历! + String userId = IP_TO_USERS_MAP.get(loginIp.trim()); + // 没找到userId,直接返回空 + if (userId == null) { + return ipUsers; } + + // 找到userId后,查用户+校验连接有效性 + WebSocketUser user = ONLINE_USER_MAP.get(userId); + if (user != null && user.isConnected()) { + ipUsers.add(user); + } + return ipUsers; } /** diff --git a/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/WebSocketInstrComm.java b/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/WebSocketInstrComm.java index 6643565..1994e8c 100644 --- a/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/WebSocketInstrComm.java +++ b/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/WebSocketInstrComm.java @@ -14,7 +14,7 @@ import java.util.Collection; /** * ws仪器通讯类 */ -@Component +//@Component @ServerEndpoint("/ws/instr/{sid}") public class WebSocketInstrComm extends WebSocketServer { diff --git a/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/WebSocketMsgServiceImpl.java b/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/WebSocketMsgServiceImpl.java index 2b7b11f..608ad5b 100644 --- a/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/WebSocketMsgServiceImpl.java +++ b/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/WebSocketMsgServiceImpl.java @@ -1,10 +1,13 @@ package com.czlis.interfaceCommon.websocket; import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; import com.czlis.common.core.redis.RedisCache; import com.czlis.common.utils.spring.SpringUtils; import org.springframework.stereotype.Service; import javax.websocket.Session; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; @@ -232,7 +235,10 @@ public class WebSocketMsgServiceImpl implements WebSocketMsgService { user.setRole(value); break; case "loginIp": - user.setLoginIp(value); + // 关键:更新loginIp时,先删旧IP索引,再加新IP索引 + ChatWebSocketServer.removeIpIndex(user.getLoginIp()); // 删旧IP + user.setLoginIp(value); // 更IP + ChatWebSocketServer.updateIpIndex(value, senderId); // 加新IP break; default: // 非固定属性,存入动态属性 @@ -240,7 +246,76 @@ public class WebSocketMsgServiceImpl implements WebSocketMsgService { break; } } - + // 4. 新增:解析并添加多IP列表 + this.addIpList(dataStrMap,senderId); System.out.println("setClientInfo成功:用户属性更新完成,senderId=" + senderId); } + /** + * 解析客户端传递的多IP列表,并同步更新IP→userId索引 + * 核心逻辑:先清理该用户所有旧IP索引 → 再添加新IP索引,避免索引残留/冗余 + * @param dataStrMap 解析后的客户端JSON数据Map + * @param userId 当前操作的用户ID(非空) + */ + public void addIpList(Map dataStrMap, String userId) { + // 1. 第一层空值校验:数据Map/用户ID为空直接返回 + if (dataStrMap == null || userId == null || userId.trim().isEmpty()) { + System.err.println("addIpList失败:dataStrMap为空 或 userId为空"); + return; + } + + // 2. 第二层空值校验:iplist字段为空直接返回 + Object ipListObj = dataStrMap.get("iplist"); + if (ipListObj == null) { + System.err.println("addIpList失败:userId=" + userId + ",iplist字段为空"); + return; + } + + try { + // 3. 兼容iplist字段的两种格式:JSON字符串/JSON对象 + String ipListJson = ipListObj instanceof String ? (String) ipListObj : JSON.toJSONString(ipListObj); + // 空JSON字符串直接返回 + if (ipListJson.trim().isEmpty()) { + System.err.println("addIpList失败:userId=" + userId + ",iplist字段值为空字符串"); + return; + } + + // 4. 解析iplist为JSON对象 + JSONObject ipListJsonObj = JSON.parseObject(ipListJson); + // 空JSON对象直接返回 + if (ipListJsonObj.isEmpty()) { + System.err.println("addIpList失败:userId=" + userId + ",iplist字段值为空JSON对象"); + return; + } + + // 5. 核心步骤1:清理该用户的所有旧IP索引(避免残留) + ChatWebSocketServer.removeUserAllIpIndex(userId); + + // 6. 核心步骤2:遍历新IP列表,同步到索引(自动去重+空值过滤) + int validIpCount = 0; // 统计有效IP数量 + for (Object ipValue : ipListJsonObj.values()) { + // 过滤null值 + if (ipValue == null) { + continue; + } + // 转为字符串并去除首尾空格 + String ip = String.valueOf(ipValue).trim(); + // 过滤空IP字符串 + if (ip.isEmpty()) { + continue; + } + // 同步到IP→userId索引 + ChatWebSocketServer.updateIpIndex(ip, userId); + validIpCount++; + } + + // 7. 日志反馈:便于排查问题 + System.out.println("addIpList成功:userId=" + userId + ",清理旧IP索引并新增" + validIpCount + "个有效IP索引"); + + } catch (Exception e) { + // 8. 异常捕获:细化异常信息,便于定位问题 + System.err.println("addIpList失败:userId=" + userId + ",iplist解析异常,异常信息:" + e.getMessage()); + // 可选:打印异常栈,便于调试 + // e.printStackTrace(); + } + } } \ No newline at end of file diff --git a/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/WebSocketUser.java b/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/WebSocketUser.java index 1b910ea..563cf9c 100644 --- a/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/WebSocketUser.java +++ b/lis-interfaceCommon/src/main/java/com/czlis/interfaceCommon/websocket/WebSocketUser.java @@ -90,4 +90,5 @@ public class WebSocketUser { public boolean isConnected() { return session != null && session.isOpen(); } + } \ No newline at end of file diff --git a/zycx/src/main/java/com/czlis/zycx/service/impl/ZycxServiceImpl.java b/zycx/src/main/java/com/czlis/zycx/service/impl/ZycxServiceImpl.java index 703754e..89c2fc5 100644 --- a/zycx/src/main/java/com/czlis/zycx/service/impl/ZycxServiceImpl.java +++ b/zycx/src/main/java/com/czlis/zycx/service/impl/ZycxServiceImpl.java @@ -327,6 +327,7 @@ public class ZycxServiceImpl implements ZycxService { public Result sendprintmsg(String msg){ String ip= IpUtils.getIpAddr(); + System.out.println("请求业务的IP:"+ip); List printUsers = ChatWebSocketServer.getOnlineUsersByLoginIp(ip); if (printUsers.isEmpty()) { System.out.println("当前客户端打印插件不在线");