最近一段时间我们在完善项目,我们在写的项目是一个商城项目。上次考核的时候考虑到进度问题,客服功能只做了一半,这次项目我对它进行了完善。
客服功能因为要实现即时通信,我主要是基于websocket实现的,它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的。因为是第一次去写类似的功能,所以在实现过程中遇到了很多问题。
离线消息:
因为客服和用户并不是一直在线的,但是在用户离线的时候别人向他发送的消息他也应该接收到。所以在发送信息的时候我加了一个判断,当用户离线的时候将接收到的信息存到redis里面,在用户连接websocket的查询redis判断用户是否有离线的信息,如果有,在登录的时候一起发送过来。
发送消息
1 2 3 4 5 6 7 8
| Session userSession = map.get("user:" + userId); if (userSession != null) { userSession.getAsyncRemote().sendText(msgJson); } { String userKey = "msg@" + message.getUserId() + ":offLine"; redisUtil.lLeftPush(userKey, msgJson); }
|
登录时判断
1 2 3 4 5
| String chatKey= "msg@" +userId + ":offLine"; while(redisUtil.hasKey(chatKey)&&redisUtil.lLen(chatKey)!=0){ String msg = redisUtil.lRightPop(chatKey); session.getAsyncRemote().sendText(msg); }
|
角色绑定:
因为店铺与客服是一对多的关系,一个店铺里面可以有多个客服在线,当用户与客服进行聊天时,要保证一个客服和一个用户进行聊天,否则不符合逻辑。所以写项目的时候我给聊天列表设置了一个状态,客服可以查看所有的聊天列表,然后选择性进行接取业务,当一个聊天列表被一个客服接取后,其他客服不能进入这个聊天列表与用户进行聊天。
因为店铺与客服的一对多的关系,所以在用户反馈的时候该店铺的所有客服都可以看到聊天记录。我在用户给店铺发送信息的时候,先查询店铺里面有哪些客服,再查询客服的session,遍历给客服发送消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
| @ServerEndpoint(value = "/websocket/{userId}") @Component public class WebSocketUtil {
private static CopyOnWriteArraySet<WebSocketUtil> webSocketSet = new CopyOnWriteArraySet<WebSocketUtil>();
private Session session;
private String key;
private static Map<String,Session> map = new HashMap<>();
private static RedisUtil redisUtil;
private static ChatDao chatDao;
@Autowired public void setRedisUtil(RedisUtil redisUtil){ WebSocketUtil.redisUtil = redisUtil; }
@Autowired public void setChatDao(ChatDao chatDao){ WebSocketUtil.chatDao = chatDao; }
@OnOpen public void onOpen(Session session,@PathParam("userId")Integer userId) { this.session = session; this.key="user:"+userId; map.put(key, session); String chatKey= "msg@" +userId + ":offLine"; while(redisUtil.hasKey(chatKey)&&redisUtil.lLen(chatKey)!=0){ String msg = redisUtil.lRightPop(chatKey); session.getAsyncRemote().sendText(msg); } webSocketSet.add(this); System.out.println("有新连接加入:"+userId+",当前在线人数为" + webSocketSet.size()); this.session.getAsyncRemote().sendText("恭喜"+userId+"成功连接上WebSocket(其频道号:"+userId+")-->当前在线人数为:"+webSocketSet.size()); }
@OnClose public void onClose() { webSocketSet.remove(this); System.out.println("有一连接关闭!当前在线人数为" + webSocketSet.size()); }
@OnMessage public void onMessage(String message,@PathParam("id") String id) { }
@OnError public void onError( Throwable error) { System.out.println("发生错误"); error.printStackTrace(); }
public void broadcast(String message){ for (WebSocketUtil item : webSocketSet) { item.session.getAsyncRemote().sendText(message); } }
public void sendMessage(Integer userId, Integer storeId, Message message) { Session userSession = map.get("user:" + userId); String msgJson = JSON.toJSONString(message); String userListKey = "userList:" + userId + "@chat"; String storeListKey = "storeList:" + storeId + "@chat"; int userList = (int) Double.parseDouble(redisUtil.zScore(userListKey, storeId + "") + ""); int storeList = (int) Double.parseDouble(redisUtil.zScore(storeListKey, userId + "") + ""); if (userSession != null) { userSession.getAsyncRemote().sendText(msgJson); } { String userKey = "msg@" + message.getUserId() + ":offLine"; redisUtil.lLeftPush(userKey, msgJson); }
String userKey = "msg@" + userList + ":message"; redisUtil.lLeftPush(userKey, msgJson); String storeKey = "msg@" + storeList + ":message"; redisUtil.lLeftPush(storeKey, msgJson); Integer chatListId; if (message.getMessageStatus() == 0) { chatListId = storeId; } else { chatListId = userId; } String numKey = "chat@No:" + chatListId + "num"; if (redisUtil.hasKey(numKey)) { redisUtil.incrBy(numKey, 1); } else { redisUtil.set(numKey, "1"); } List<Integer> list=null; if(redisUtil.hasKey("chatService:"+storeId)){ Set<String> strings = redisUtil.setMembers("chatService:" + storeId); list = strings.stream().map(Integer::parseInt).collect(Collectors.toList()); } if (list != null) { for (Integer id : list) { Session chatSession = map.get("user:" + id); String chatKey; if (chatSession != null) { chatSession.getAsyncRemote().sendText(msgJson); } else { chatKey = "msg@" + message.getUserId() + ":offLine"; redisUtil.lLeftPush(chatKey, msgJson); } } } }
|