This commit is contained in:
tangw 2026-01-25 22:23:57 +08:00
parent 652263c057
commit 96255ab9e6
5 changed files with 140 additions and 10 deletions

View File

@ -33,6 +33,7 @@ public class ChatWebSocketServer {
private static final Set<String> CORE_PARAMS = new HashSet<>(Arrays.asList("token", "username", "role")); private static final Set<String> CORE_PARAMS = new HashSet<>(Arrays.asList("token", "username", "role"));
// 🌟 容器改为存储封装后的WebSocketUser(key=userId) // 🌟 容器改为存储封装后的WebSocketUser(key=userId)
private static final Map<String, WebSocketUser> ONLINE_USER_MAP = new ConcurrentHashMap<>(); private static final Map<String, WebSocketUser> ONLINE_USER_MAP = new ConcurrentHashMap<>();
private static final Map<String, String> IP_TO_USERS_MAP = new ConcurrentHashMap<>();
/** /**
* 连接建立时:封装用户信息并存入容器 * 连接建立时:封装用户信息并存入容器
* 注意:若需要传递username/role等属性,可通过路径参数/请求参数/Token解析 * 注意:若需要传递username/role等属性,可通过路径参数/请求参数/Token解析
@ -79,6 +80,9 @@ public class ChatWebSocketServer {
} }
// 5. 存入全局容器 // 5. 存入全局容器
ONLINE_USER_MAP.put(userId, webSocketUser); 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()); System.out.println("用户[" + userId + "-" + username + "]连接成功,IP:" + loginIp + ",当前在线人数:" + ONLINE_USER_MAP.size());
} }
// ========== 消息接收核心方法 ========== // ========== 消息接收核心方法 ==========
@ -166,6 +170,7 @@ public class ChatWebSocketServer {
@OnClose @OnClose
public void onClose(@PathParam("userId") String userId, Session session) { public void onClose(@PathParam("userId") String userId, Session session) {
ONLINE_USER_MAP.remove(userId); ONLINE_USER_MAP.remove(userId);
ChatWebSocketServer.removeUserAllIpIndex(userId);
System.out.println("用户[" + userId + "]断开连接,当前在线人数:" + ONLINE_USER_MAP.size()); 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) { public void onError(@PathParam("userId") String userId, Session session, Throwable throwable) {
throwable.printStackTrace(); throwable.printStackTrace();
ONLINE_USER_MAP.remove(userId); ONLINE_USER_MAP.remove(userId);
ChatWebSocketServer.removeUserAllIpIndex(userId);
} }
// ========== 工具方法:获取客户端IP ========== // ========== 工具方法:获取客户端IP ==========
@ -188,7 +194,38 @@ public class ChatWebSocketServer {
return "未知IP"; 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<String> userIps = new ArrayList<>();
for (Map.Entry<String, String> 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; 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筛选在线用户(精确匹配) * 根据登录IP筛选在线用户(精确匹配)
* @param loginIp 要查询的登录IP(如 "192.168.1.100") * @param loginIp 要查询的登录IP(如 "192.168.1.100")
@ -225,17 +271,24 @@ public class ChatWebSocketServer {
*/ */
public static List<WebSocketUser> getOnlineUsersByLoginIp(String loginIp) { public static List<WebSocketUser> getOnlineUsersByLoginIp(String loginIp) {
List<WebSocketUser> ipUsers = new ArrayList<>(); List<WebSocketUser> ipUsers = new ArrayList<>();
// 前置空值校验:避免传入null/空字符串导致无效查询 // 前置空值校验
if (loginIp == null || loginIp.trim().isEmpty()) { if (loginIp == null || loginIp.trim().isEmpty()) {
return ipUsers; return ipUsers;
} }
// 遍历在线用户,筛选匹配IP且连接有效的用户
for (WebSocketUser user : ONLINE_USER_MAP.values()) { // 核心改造:直接从索引Map拿userId,无需遍历!
// 双重校验:IP精确匹配 + 连接有效 String userId = IP_TO_USERS_MAP.get(loginIp.trim());
if (loginIp.equals(user.getLoginIp()) && user.isConnected()) { // 没找到userId,直接返回空
ipUsers.add(user); if (userId == null) {
} return ipUsers;
} }
// 找到userId后,查用户+校验连接有效性
WebSocketUser user = ONLINE_USER_MAP.get(userId);
if (user != null && user.isConnected()) {
ipUsers.add(user);
}
return ipUsers; return ipUsers;
} }
/** /**

View File

@ -14,7 +14,7 @@ import java.util.Collection;
/** /**
* ws仪器通讯类 * ws仪器通讯类
*/ */
@Component //@Component
@ServerEndpoint("/ws/instr/{sid}") @ServerEndpoint("/ws/instr/{sid}")
public class WebSocketInstrComm extends WebSocketServer { public class WebSocketInstrComm extends WebSocketServer {

View File

@ -1,10 +1,13 @@
package com.czlis.interfaceCommon.websocket; package com.czlis.interfaceCommon.websocket;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.czlis.common.core.redis.RedisCache; import com.czlis.common.core.redis.RedisCache;
import com.czlis.common.utils.spring.SpringUtils; import com.czlis.common.utils.spring.SpringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.websocket.Session; import javax.websocket.Session;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -232,7 +235,10 @@ public class WebSocketMsgServiceImpl implements WebSocketMsgService {
user.setRole(value); user.setRole(value);
break; break;
case "loginIp": case "loginIp":
user.setLoginIp(value); // 关键:更新loginIp时,先删旧IP索引,再加新IP索引
ChatWebSocketServer.removeIpIndex(user.getLoginIp()); // 删旧IP
user.setLoginIp(value); // 更IP
ChatWebSocketServer.updateIpIndex(value, senderId); // 加新IP
break; break;
default: default:
// 非固定属性,存入动态属性 // 非固定属性,存入动态属性
@ -240,7 +246,76 @@ public class WebSocketMsgServiceImpl implements WebSocketMsgService {
break; break;
} }
} }
// 4. 新增:解析并添加多IP列表
this.addIpList(dataStrMap,senderId);
System.out.println("setClientInfo成功:用户属性更新完成,senderId=" + senderId); System.out.println("setClientInfo成功:用户属性更新完成,senderId=" + senderId);
} }
/**
* 解析客户端传递的多IP列表,并同步更新IP→userId索引
* 核心逻辑:先清理该用户所有旧IP索引 → 再添加新IP索引,避免索引残留/冗余
* @param dataStrMap 解析后的客户端JSON数据Map
* @param userId 当前操作的用户ID(非空)
*/
public void addIpList(Map<String, Object> 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();
}
}
} }

View File

@ -90,4 +90,5 @@ public class WebSocketUser {
public boolean isConnected() { public boolean isConnected() {
return session != null && session.isOpen(); return session != null && session.isOpen();
} }
} }

View File

@ -327,6 +327,7 @@ public class ZycxServiceImpl implements ZycxService {
public Result sendprintmsg(String msg){ public Result sendprintmsg(String msg){
String ip= IpUtils.getIpAddr(); String ip= IpUtils.getIpAddr();
System.out.println("请求业务的IP:"+ip);
List<WebSocketUser> printUsers = ChatWebSocketServer.getOnlineUsersByLoginIp(ip); List<WebSocketUser> printUsers = ChatWebSocketServer.getOnlineUsersByLoginIp(ip);
if (printUsers.isEmpty()) { if (printUsers.isEmpty()) {
System.out.println("当前客户端打印插件不在线"); System.out.println("当前客户端打印插件不在线");