智能协同云图库-进入编辑报错解决
问题复现
点击进入编辑,页面没变化

F12 打开控制台,两条消息,连接已建立,然后又连接已断开

后端控制台报错,LocalDataTime 没有办法序列化

因为 user 的时间字段被我改成了 LocalDateTime,而 LocalDateTime 是需要手动添加序列化器的,因为异常最终导致 WebSocket 关闭。
解决
使用 Spring 提供的 ObjectMapper 代替自己new
▼java复制代码/** * 复用 Spring 管理的 ObjectMapper,统一支持 Long 和 LocalDateTime 序列化。 */ @Resource private ObjectMapper objectMapper;

▼java复制代码/** * 将响应信息广播给 集合里的session * * @param pictureId * @param pictureEditResponseMessage * @param excludeSession * @throws Exception */ private void broadcastToPicture(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage, WebSocketSession excludeSession) throws Exception { Set<WebSocketSession> sessionSet = pictureSessions.get(pictureId); if (CollectionUtils.isEmpty(sessionSet)) { return; } // 使用 Spring 管理的 ObjectMapper,避免裸 ObjectMapper 无法处理 LocalDateTime。 String message = objectMapper.writeValueAsString(pictureEditResponseMessage); for (WebSocketSession session : sessionSet) { // 排除掉 session if (session.equals(excludeSession)) { continue; } if (session.isOpen()) { sendTextMessage(session, message); } } } /** * 向当前连接发送错误消息。 */ public void sendErrorMessage(WebSocketSession session, User user, String errorMessage) throws Exception { PictureEditResponseMessage responseMessage = new PictureEditResponseMessage(); responseMessage.setType(PictureEditMessageTypeEnum.ERROR.getValue()); responseMessage.setMessage(errorMessage); if (user != null) { responseMessage.setUser(userService.getUserVO(user)); } String message = objectMapper.writeValueAsString(responseMessage); sendTextMessage(session, message); } /** * WebSocketSession 不保证并发发送安全,统一同步发送。 */ private void sendTextMessage(WebSocketSession session, String message) throws Exception { if (session == null || !session.isOpen()) { return; } synchronized (session) { if (session.isOpen()) { session.sendMessage(new TextMessage(message)); } } }
接收前端发来的消息,也要 使用 objectMapper 转化为对象
▼java复制代码/** * 收到前端的信息后处理 * * @param session * @param message * @throws Exception */ @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { User user = (User) session.getAttributes().get("user"); Long pictureId = (Long) session.getAttributes().get("pictureId"); PictureEditRequestMessage pictureEditRequestMessage; try { // TextMessage#getPayload() 是 JSON 字符串,必须使用 JSON 反序列化。 pictureEditRequestMessage = objectMapper.readValue( message.getPayload(), PictureEditRequestMessage.class ); } catch (JsonProcessingException e) { log.warn("图片编辑消息解析失败,sessionId={},payload={}", session.getId(), message.getPayload(), e); sendErrorMessage(session, user, "消息格式错误"); return; } PictureEditMessageTypeEnum messageTypeEnum = PictureEditMessageTypeEnum.getEnumByValue(pictureEditRequestMessage.getType()); if (messageTypeEnum == null) { sendErrorMessage(session, user, "消息类型错误"); return; } // 生产消息 pictureEditEventProducer.publishEvent( pictureEditRequestMessage, session, user, pictureId ); }
完整代码
▼java复制代码import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.my.picturesystembackend.manager.websocket.disruptor.PictureEditEventProducer; import com.my.picturesystembackend.manager.websocket.model.PictureEditActionEnum; import com.my.picturesystembackend.manager.websocket.model.PictureEditMessageTypeEnum; import com.my.picturesystembackend.manager.websocket.model.PictureEditRequestMessage; import com.my.picturesystembackend.manager.websocket.model.PictureEditResponseMessage; import com.my.picturesystembackend.model.entity.User; import com.my.picturesystembackend.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; 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 javax.annotation.Resource; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * 图片编辑处理 */ @Component @Slf4j public class PictureEditHandler extends TextWebSocketHandler { @Resource private UserService userService; @Resource private PictureEditEventProducer pictureEditEventProducer; /** * 复用 Spring 管理的 ObjectMapper,统一支持 Long 和 LocalDateTime 序列化。 */ @Resource private ObjectMapper objectMapper; // 当前正在编辑的图片的用户:key:pictureId value: 当前正在编辑的 userId private static final Map<Long, Long> pictureEditingUsers = new ConcurrentHashMap<>(); // 保存连接的所有会话,key: pictureId, value: 用户会话集合 private static final Map<Long, Set<WebSocketSession>> pictureSessions = new ConcurrentHashMap<>(); /** * 连接建立成功后保存信息 * * @param session * @throws Exception */ @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { super.afterConnectionEstablished(session); // 保存用户信息 User user = (User) session.getAttributes().get("user"); Long pictureId = (Long) session.getAttributes().get("pictureId"); // session 加到集合中 pictureSessions.computeIfAbsent(pictureId, key -> ConcurrentHashMap.newKeySet()).add(session); // 发送广播信息,通知在这个集合的所有用户,who 加进来了,相当于加入房间 PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage(); pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue()); String message = String.format("用户 %s 加入编辑", user.getUserName()); pictureEditResponseMessage.setMessage(message); pictureEditResponseMessage.setUser(userService.getUserVO(user)); // 广播消息 broadcastToPicture(pictureId, pictureEditResponseMessage); } /** * 收到前端的信息后处理 * * @param session * @param message * @throws Exception */ @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { User user = (User) session.getAttributes().get("user"); Long pictureId = (Long) session.getAttributes().get("pictureId"); PictureEditRequestMessage pictureEditRequestMessage; try { // TextMessage#getPayload() 是 JSON 字符串,必须使用 JSON 反序列化。 pictureEditRequestMessage = objectMapper.readValue( message.getPayload(), PictureEditRequestMessage.class ); } catch (JsonProcessingException e) { log.warn("图片编辑消息解析失败,sessionId={},payload={}", session.getId(), message.getPayload(), e); sendErrorMessage(session, user, "消息格式错误"); return; } PictureEditMessageTypeEnum messageTypeEnum = PictureEditMessageTypeEnum.getEnumByValue(pictureEditRequestMessage.getType()); if (messageTypeEnum == null) { sendErrorMessage(session, user, "消息类型错误"); return; } // 生产消息 pictureEditEventProducer.publishEvent( pictureEditRequestMessage, session, user, pictureId ); } /** * 进入编辑状态 * * @param pictureEditRequestMessage * @param session * @param user * @param pictureId */ public void handleEnterEditMessage(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws Exception { // 一张图片同时只能有一个用户在编辑 // 判断是否有其他人在编辑 if (!pictureEditingUsers.containsKey(pictureId)) { // 加入编辑 pictureEditingUsers.put(pictureId, user.getId()); // 发送 广播 通知其他用户我已经进入编辑状态 PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage(); pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.ENTER_EDIT.getValue()); pictureEditResponseMessage.setMessage(String.format(" %s 开始编辑图片", user.getUserName())); pictureEditResponseMessage.setUser(userService.getUserVO(user)); broadcastToPicture(pictureId, pictureEditResponseMessage); } } /** * 处理编辑动作 * * @param pictureEditRequestMessage * @param session * @param user * @param pictureId */ public void handleEditActionMessage(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws Exception { // 判断当前正在编辑的用户是不是自己 Long currentEditUserId = pictureEditingUsers.get(pictureId); String editAction = pictureEditRequestMessage.getEditAction(); PictureEditActionEnum pictureEditActionEnum = PictureEditActionEnum.getEnumByValue(editAction); if (pictureEditActionEnum == null) { return; } if (currentEditUserId != null && currentEditUserId.equals(user.getId())) { // 是本人发送编辑通知 PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage(); pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.EDIT_ACTION.getValue()); pictureEditResponseMessage.setMessage(String.format("%s 执行了 %s 操作", user.getUserName(), pictureEditActionEnum.getText())); pictureEditResponseMessage.setEditAction(editAction); pictureEditResponseMessage.setUser(userService.getUserVO(user)); // 广播给处了自己以外的其他用户 broadcastToPicture(pictureId, pictureEditResponseMessage, session); } } /** * 退出编辑 * * @param pictureEditRequestMessage * @param session * @param user * @param pictureId */ public void handleExitEditMessage(PictureEditRequestMessage pictureEditRequestMessage, WebSocketSession session, User user, Long pictureId) throws Exception { // 校验当前正在编辑的用户是不是自己 Long currentEditUserId = pictureEditingUsers.get(pictureId); if (currentEditUserId != null && currentEditUserId.equals(user.getId())) { pictureEditingUsers.remove(pictureId); // 发送消息通知 PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage(); pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.EXIT_EDIT.getValue()); pictureEditResponseMessage.setMessage(String.format("%s 退出编辑图片", user.getUserName())); pictureEditResponseMessage.setUser(userService.getUserVO(user)); broadcastToPicture(pictureId, pictureEditResponseMessage); } } /** * 关闭连接的处理 * * @param session * @param status * @throws Exception */ @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { super.afterConnectionClosed(session, status); // 判断该用户是否正在编辑,如果是需要退出 // 从 session 中获取通用参数 User user = (User) session.getAttributes().get("user"); Long pictureId = (Long) session.getAttributes().get("pictureId"); handleExitEditMessage(null, session, user, pictureId); // 移除session Set<WebSocketSession> webSocketSessions = pictureSessions.get(pictureId); if (webSocketSessions != null) { webSocketSessions.remove(session); if (webSocketSessions.isEmpty()) { pictureSessions.remove(pictureId); } } // 发送消息退出编辑 PictureEditResponseMessage pictureEditResponseMessage = new PictureEditResponseMessage(); pictureEditResponseMessage.setType(PictureEditMessageTypeEnum.INFO.getValue()); pictureEditResponseMessage.setMessage(String.format("%s离开编辑", user.getUserName())); pictureEditResponseMessage.setUser(userService.getUserVO(user)); broadcastToPicture(pictureId, pictureEditResponseMessage); } /** * 将响应信息广播给 集合里的session * * @param pictureId * @param pictureEditResponseMessage * @param excludeSession * @throws Exception */ private void broadcastToPicture(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage, WebSocketSession excludeSession) throws Exception { Set<WebSocketSession> sessionSet = pictureSessions.get(pictureId); if (CollectionUtils.isEmpty(sessionSet)) { return; } // 使用 Spring 管理的 ObjectMapper,避免裸 ObjectMapper 无法处理 LocalDateTime。 String message = objectMapper.writeValueAsString(pictureEditResponseMessage); for (WebSocketSession session : sessionSet) { // 排除掉 session if (session.equals(excludeSession)) { continue; } if (session.isOpen()) { sendTextMessage(session, message); } } } /** * 向当前连接发送错误消息。 */ public void sendErrorMessage(WebSocketSession session, User user, String errorMessage) throws Exception { PictureEditResponseMessage responseMessage = new PictureEditResponseMessage(); responseMessage.setType(PictureEditMessageTypeEnum.ERROR.getValue()); responseMessage.setMessage(errorMessage); if (user != null) { responseMessage.setUser(userService.getUserVO(user)); } String message = objectMapper.writeValueAsString(responseMessage); sendTextMessage(session, message); } /** * WebSocketSession 不保证并发发送安全,统一同步发送。 */ private void sendTextMessage(WebSocketSession session, String message) throws Exception { if (session == null || !session.isOpen()) { return; } synchronized (session) { if (session.isOpen()) { session.sendMessage(new TextMessage(message)); } } } /** * 全体广播不排除 * * @param pictureId * @param pictureEditResponseMessage * @throws Exception */ private void broadcastToPicture(Long pictureId, PictureEditResponseMessage pictureEditResponseMessage) throws Exception { broadcastToPicture(pictureId, pictureEditResponseMessage, null); } }
评论
问答助学
相关内容
0个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
