自媒体时代做网站有前途吗google网址直接打开
- 作者: 五速梦信息网
- 时间: 2026年03月21日 05:00
当前位置: 首页 > news >正文
自媒体时代做网站有前途吗,google网址直接打开,wordpress手机主题浮动导航,wordpress sql文件大小实现功能 用户注册和登录好友列表展示会话列表展示: 显示当前正在进行哪些会话 (单聊 / 群聊) , 选中好友列表中的某个好友, 会生成对应的会话实时通信, A给B发送消息, B的聊天界面 / 会话界面能立刻显示新的消息 TODO: 添加好友功能用户头像显示传输图片 / 表情包历史消息搜…实现功能 用户注册和登录好友列表展示会话列表展示: 显示当前正在进行哪些会话 (单聊 / 群聊) , 选中好友列表中的某个好友, 会生成对应的会话实时通信, A给B发送消息, B的聊天界面 / 会话界面能立刻显示新的消息 TODO: 添加好友功能用户头像显示传输图片 / 表情包历史消息搜索消息撤回… 相关技术 网络通信: WebSocket Spring SpringBoot SpringMVC MyBatis HTML CSS JS 数据库设计 项目的基本框架 前端页面 注册和登录页面 聊天界面 后端代码 实体类 User 本类表示一个用户的信息, 对应数据库的 user 表 Data public class User {private int userId;private String username ;private String password ;public User() {}public User(String username, String password) {this.username username;this.password password;} }Friend 使用一个 Friend 对象表示一个好友 // 使用一个 Friend 对象表示一个好友, 对应数据库的 friend 表 Data public class Friend {private int friendId;private String friendName;public Friend() {}public Friend(int friendId, String friendName) {this.friendId friendId;this.friendName friendName;} }Message 本类表示一条消息的相关信息, 对应数据库的表 message 字段: fromname (没有 postTime 是因为: 在查询的时候就是一次性查出所有的时间, 按照时间结果排序后返回, 我们这里就不需要再获取时间了) // 本类表示一条消息的相关信息 // (没有 postTime 是因为: 在查询的时候就是一次性查出所有的时间, 按照时间结果排序后返回, 我们这里就不需要再获取时间了) Data public class Message {private Integer messageId;private int fromId;private String fromName;private int sessionId;private String content;public Message() {}public Message( int fromId, String fromName, int sessionId, String content) {this.fromId fromId;this.fromName fromName;this.sessionId sessionId;this.content content;} }MessageSession 使用该类表示一个会话, 对应数据库的 message_session message_session_user // 使用该类表示一个会话 Data public class MessageSession {private int sessionId;private ListFriend friends;private String lastMessage; }MessageSessionUserItem 该类对象表示 message_session_user 表里的一个记录 // 该类对象表示 message_session_user 表里的一个记录 Data public class MessageSessionUserItem {private int sessionId;private int userId;public MessageSessionUserItem() {}public MessageSessionUserItem(int sessionId, int userId) {this.sessionId sessionId;this.userId userId;} }MessageRequest WebSocket 请求 自定义格式, 用于网络通信中接受请求 // WebSocket请求 Data public class MessageRequest {private String type message;private int sessionId;private String content; }MessageResponse WebSocket 响应 自定义格式, 用于网络通信中返回响应 // WebSocket响应 Data public class MessageResponse {private String type message;private int fromId;private String fromName;private int sessionId;private String content;public MessageResponse() {}public MessageResponse(int fromId, String fromName, int sessionId, String content) {this.fromId fromId;this.fromName fromName;this.sessionId sessionId;this.content content;} }数据库 FriendMapper 用户好友的相关操作 Mapper public interface FriendMapper {// 查询用户好友列表ListFriend selectFriendList(Param(userId) int userId); }?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.java_chatroom.model.FriendMapperselect idselectFriendList resultTypecom.example.java_chatroom.model.Friendselect userId as friendId, username as friendNamefrom userwhere userId in(select friendId from friend where userId #{userId})/select /mapperMessageMapper 消息的相关操作 Mapper public interface MessageMapper {// 获取指定会话的最后一条消息String getLastMessageBySessionId(Param(sessionId) int sessionId);// 获取指定会话的历史消息 (限制100条)ListMessage getMessagesBySessionId(Param(sessionId) int sessionId);// 插入一条消息到数据库表中void add(Param(message) Message message); }?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.java_chatroom.model.MessageMapperselect idgetLastMessageBySessionId resultTypejava.lang.Stringselect content from messagewhere sessionId #{sessionId}order by postTime desclimit 1/selectselect idgetMessagesBySessionId resultTypecom.example.java_chatroom.model.MessageselectmessageId, sessionId, fromId, content, username as fromNamefrommessage, userwheresessionId #{sessionId}and fromId userIdorder bypostTime desclimit 100 offset 0/selectinsert idaddinsert into message values(null, #{message.fromId}, #{message.sessionId}, #{message.content}, now());/insert /mapperMessageSessionMapper 会话的相关操作 Mapper public interface MessageSessionMapper {// 1.根据 userId 获取到该用户在哪些会话中存在, 返回结果是一组 sessionId.ListInteger getSessionIdsByUserId(Param(userId) int userId);// 2. 根据 sessionId 查询这个会话包含哪些用户(刨除掉最初的 user)ListFriend getFriendsBySessionId(Param(sessionId) int sessionId,Param(selfUserId) int selfUserId);// 3. 新增会话记录, 返回会话 idint addMessageSession(Param(messageSession) MessageSession messageSession);// 4.给 message_session_user 表新增对应记录int addMessageSessionUser(Param(messageSessionUserItem) MessageSessionUserItem messageSessionUserItem); }?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.java_chatroom.model.MessageSessionMapperselect idgetSessionIdsByUserId resultTypejava.lang.Integerselect sessionId from message_sessionwhere sessionId in( select sessionId from message_session_userwhere userId #{userId} )order by lastTime desc/selectselect idgetFriendsBySessionId resultTypecom.example.java_chatroom.model.Friendselect userId as friendId, username as friendNamefrom userwhere userId in( select userId from message_session_userwhere sessionId #{sessionId}and userId ! #{selfUserId} )/selectinsert idaddMessageSession useGeneratedKeystrue keyPropertymessageSession.sessionIdinsert into message_session values(null, now())/insertinsert idaddMessageSessionUserinsert into message_session_user values(#{messageSessionUserItem.sessionId},#{messageSessionUserItem.userId})/insert /mapperUserMapper 用户的相关操作 Mapper public interface UserMapper {// 把用户插入到数据库中 - 注册int insert(Param(user) User user);// 根据用户名查询用户信息 - 登录Select(select * from user where username #{username})User selectByName(Param(username) String username); }?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.java_chatroom.model.UserMapperinsert idinsert useGeneratedKeystrue keyPropertyuserIdinsert into user values(null, #{user.username}, #{user.password})/insert /mapperWebSocket 通讯模块 前端 主要是 JS 中的代码 先放一个 demo // 编写 js 使用 websocket 的代码. // 创建一个 websocket 实例 let websocket new WebSocket(ws://127.0.0.1:8080/test);// 给这个 websocket 注册上一些回调函数. websocket.onopen function() {// 连接建立完成后, 就会自动执行到. console.log(websocket 连接成功!); }websocket.onclose function() {// 连接断开后, 自动执行到. console.log(websocket 连接断开!); } websocket.onerror function() {// 连接异常时, 自动执行到console.log(websocket 连接异常!); }websocket.onmessage function(e) {// 收到消息时, 自动执行到console.log(websocket 收到消息! e.data); }// 发送消息 (点击发送按钮之后触发的事件) let messageInput document.querySelector(#message); let sendButton document.querySelector(#send-button); sendButton.onclick function() {console.log(websocket 发送消息: messageInput.value);websocket.send(messageInput.value); }这里就是本项目前端使用 WebSocket 进行网络通信的逻辑 / // 操作 websocket /// 创建 websocket 实例 // let websocket new WebSocket(ws://127.0.0.1:8080/WebSocketMessage); // let websocket new WebSocket(ws://152.136.56.110:9090/WebSocketMessage); let websocket new WebSocket(ws:// location.host /WebSocketMessage);websocket.onopen function() {console.log(websocket 连接成功!); }websocket.onmessage function(e) {console.log(websocket 收到消息! e.data);// 此时收到的 e.data 是个 json 字符串, 需要转成 js 对象let resp JSON.parse(e.data);if (resp.type message) {// 处理消息响应handleMessage(resp);} else {// resp 的 type 出错!console.log(resp.type 不符合要求!);} }websocket.onclose function() {console.log(websocket 连接关闭!); }websocket.onerror function() {console.log(websocket 连接异常!); }function handleMessage(resp) {// 把客户端收到的消息, 给展示出来. // 展示到对应的会话预览区域, 以及右侧消息列表中. // 1. 根据响应中的 sessionId 获取到当前会话对应的 li 标签. // 如果 li 标签不存在, 则创建一个新的let curSessionLi findSessionLi(resp.sessionId);if (curSessionLi null) {// 就需要创建出一个新的 li 标签, 表示新会话. curSessionLi document.createElement(li);curSessionLi.setAttribute(message-session-id, resp.sessionId);// 此处 p 标签内部应该放消息的预览内容. 一会后面统一完成, 这里先置空curSessionLi.innerHTML h3 resp.fromName /h3 p/p;// 给这个 li 标签也加上点击事件的处理curSessionLi.onclick function() {clickSession(curSessionLi);}}// 2. 把新的消息, 显示到会话的预览区域 (li 标签里的 p 标签中)// 如果消息太长, 就需要进行截断. let p curSessionLi.querySelector(p);p.innerHTML resp.content;if (p.innerHTML.length 10) {p.innerHTML p.innerHTML.substring(0, 10) …;}// 3. 把收到消息的会话, 给放到会话列表最上面. let sessionListUL document.querySelector(#session-list);sessionListUL.insertBefore(curSessionLi, sessionListUL.children[0]);// 4. 如果当前收到消息的会话处于被选中状态, 则把当前的消息给放到右侧消息列表中. // 新增消息的同时, 注意调整滚动条的位置, 保证新消息虽然在底部, 但是能够被用户直接看到. if (curSessionLi.className selected) {// 把消息列表添加一个新消息. let messageShowDiv document.querySelector(.right .message-show);addMessage(messageShowDiv, resp);scrollBottom(messageShowDiv);}// 其他操作, 还可以在会话窗口上给个提示 (红色的数字, 有几条消息未读), 还可以播放个提示音. // 这些操作都是纯前端的. 实现也不难, 不是咱们的重点工作. 暂时不做了. }function findSessionLi(targetSessionId) {// 获取到所有的会话列表中的 li 标签let sessionLis document.querySelectorAll(#session-list li);for (let li of sessionLis) {let sessionId li.getAttribute(message-session-id);if (sessionId targetSessionId) {return li;}}// 啥时候会触发这个操作, 就比如如果当前新的用户直接给当前用户发送消息, 此时没存在现成的 li 标签return null; }后端 同样先上 Demo Component public class TestWebSocketAPI extends TextWebSocketHandler {Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {// 该方法会在 websocket 连接建立之后, 被自动调用System.out.println(Test 连接成功!);}Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {// 该方法会在 websocket 收到消息的时候, 被自动调用System.out.println(Test 收到消息! message.toString());// session 是个会话, 里面记录通信双方的信息 (session 中持有 websocket 的通信连接)session.sendMessage(message);}Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {// 这个方法实在 连接出现异常的时候, 被自动调用System.out.println(Test 连接异常!);}Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {// 这个方法是在连接正常关闭后, 会被自动调用System.out.println(Test 连接关闭!);} }下面是本项目中后端使用 WebSocket 实现网络通信 创建 Handler 对象 Slf4j Component public class WebSocketAPI extends TextWebSocketHandler {Autowiredprivate OnlineUserMapper onlineUserMapper;Autowiredprivate MessageSessionMapper messageSessionMapper;Autowiredprivate MessageMapper messageMapper;// 自己创建对象也行, 使用 Autowired 注入也行, spring 本身就有内置对象 ObjectMapperprivate ObjectMapper objectMapper new ObjectMapper();Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {log.info([WebSocketAPI] 连接成功!);User user (User) session.getAttributes().get(user);if(user null) {return;}log.info(获取到的 userId: {}, username: {},user.getUserId(), user.getUsername());// 连接建立成功之后, 将 上线用户 和 session 进行绑定onlineUserMapper.online(user.getUserId(), session);}/*** 数据处理* param session* param message* throws Exception/Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {log.info([WebSocketAPI] 收到消息! message.toString());// 先获取到当前用户的信息, 后续要转发的消息等User user (User) session.getAttributes().get(user);if(user null){log.info([WebSocketAPI] user null, 未登录用户, 无法进行消息转发);return;}// 针对请求进行解析, 把 json 格式字符串转换成 Java 对象MessageRequest req objectMapper.readValue(message.getPayload(), MessageRequest.class);if(message.equals(req.getType())) {// 进行消息转发transferMessage(user, req);}else {log.info([WebSocketAPI] req.type 有误! {}, message.getPayload());}}/** 通过该方法来完成消息的实际转发过程* param user 发送消息的对象* param req 内含 sessionId, content/private void transferMessage(User user, MessageRequest req) throws IOException {// 先构造一个待转发的响应对象. MessageResponseMessageResponse resp new MessageResponse(user.getUserId(), user.getUsername(), req.getSessionId(), req.getContent());// 把这个响应对象转换成 JSON 格式字符串,以待备用String respJson objectMapper.writeValueAsString(resp);log.info([transferMessage] respJson: {}, respJson);// 根据请求中的 sessionId, 获取到 MessageSession 里有哪些用户 (查询数据库)ListFriend friends messageSessionMapper.getFriendsBySessionId(req.getSessionId(), user.getUserId());// 此处响应返回的对象中, 应该包含发送方Friend myself new Friend(user.getUserId(), user.getUsername());friends.add(myself);// 循环遍历 friends, 给其中每一个对象都发送一份响应// 这里是为了满足群聊的设定(即使前端还未实现,但是后端接口和数据库都是支持群聊的)for(Friend friend : friends) {// 已知 userId, 进一步查询 OnlineUserMapper, 获取对应的 WebSocketSession, 从而进行消息转发WebSocketSession webSocketSession onlineUserMapper.getSession(friend.getFriendId());if(webSocketSession ! null) {webSocketSession.sendMessage(new TextMessage(respJson));}}// 转发的消息还要在数据库备份Message message new Message(user.getUserId(), user.getUsername(), req.getSessionId(), resp.getContent());// 自增主键为 null或为空, 数据库会自动生成messageMapper.add(message);}Overridepublic void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {log.info([WebSocketAPI] 连接异常! exception.toString());User user (User) session.getAttributes().get(user);if(user ! null) {onlineUserMapper.offline(user.getUserId(), session);}}Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {log.info([WebSocketAPI] 连接关闭! status.toString());User user (User) session.getAttributes().get(user);if(user ! null) {onlineUserMapper.offline(user.getUserId(), session);}} }将 Handler 注册到 Config 里面 Configuration EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer {Autowiredprivate TestWebSocketAPI testWebSocketAPI;Autowiredprivate WebSocketAPI webSocketAPI;Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {// 通过本方法, 将创建好的 Handler 类给注册到具体路径上.// 此时浏览器可通过 请求路径, 调用到绑定的 Handler 类.registry.addHandler(testWebSocketAPI, /test);registry.addHandler(webSocketAPI, /WebSocketMessage)// 通过注册这个特定的 HttpSession 拦截器, 可以把用户在// HttpSession 中添加的 Attribute 键值对// 往 WebSocketSession 中添加一份.addInterceptors(new HttpSessionHandshakeInterceptor());} }OnlineUserMapper 本类用来记录当前用户在线的状态. (维护 userId 和 WebSocketSession 之间的映射) // 本类用来记录当前用户在线的状态. (维护 userId 和 WebSocketSession 之间的映射) Slf4j Component public class OnlineUserMapper {// 此处这个哈希表要考虑 线程安全 问题private ConcurrentHashMapInteger, WebSocketSession sessions new ConcurrentHashMap();/** 用户上线, 给哈希表里插入键值对* param userId* param webSocketSession/public void online(int userId, WebSocketSession webSocketSession) {if(sessions.get(userId) ! null) {// 针对用户多开, 这里的处理是不记录后面登录用户的 session, 即后续登录用户做不到消息的收发// (毕竟这里是根据映射关系来实现消息转发的)log.info([{}] 已登录, 登录失败,userId);return;}sessions.put(userId, webSocketSession);log.info([{}] 上线!, userId);}/** 用户下线, 根据 userId 删除键值对* param userId* param webSocketSession/public void offline(int userId, WebSocketSession webSocketSession) {if(sessions.get(userId) webSocketSession) {// 如果键值对中 session和调用该方法的 session 相同, 才允许删除键值对sessions.remove(userId);log.info([{}] 下线!, userId);}}/** 根据 userId 获取键值对** param userId* return/public WebSocketSession getSession(int userId) {return sessions.get(userId);} }功能处理 用户注册 调用接口: register Slf4j RestController Controller ResponseBody public class UserAPI {Resourceprivate UserMapper userMapper;/** 用户注册* 返回 User 对象* 注册成功, 返回的 User 对象包含用户信息* 注册失败, 返回的 User 对象无内容/RequestMapping(/register)public Object register(String username, String password) {User user new User();// 判空if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return user;}try {user new User(username, password);int ret userMapper.insert(user);log.info(注册 ret :{}, ret);user.setPassword();} catch (DuplicateKeyException e) {// 抛出该异常说明用户名重复, 注册失败user new User();log.error(用户名重复, 注册失败);}return user;} }用户登录 调用接口: login Slf4j RestController Controller ResponseBody public class UserAPI {Resourceprivate UserMapper userMapper;/** 用户登录* 返回 User 对象* 登录成功, 返回的 User 对象包含用户信息, 并且将 User 对象存储在 session 中* 登录失败, 返回的 User 对象无内容/RequestMapping(/login)public Object login(String username, String password, HttpServletRequest request) {// 判空if(!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {return new User();}// 校验用户名密码User user userMapper.selectByName(username);if(user null || !password.equals(user.getPassword())) {return new User();}// 校验成功, 则登陆成功, 创建会话// true 表示会话不存在则创建会话, false 表示会话不存在就返回空HttpSession session request.getSession(true);session.setAttribute(user,user);user.setPassword();return user;} }用户登录后, 聊天界面会自动获取登录用户的好友列并展示 调用接口: friendList // 处理好友信息 Slf4j RestController public class FriendAPI {Resourceprivate FriendMapper friendMapper;RequestMapping(/friendList)public Object getFriendList(HttpServletRequest req) {// 1. 先从会话中, 获取到 userIdHttpSession session req.getSession(false);if(session null) {log.info([getFriendList] session 不存在);return new ArrayListFriend();}User user (User) session.getAttribute(user);if(user null) {log.info([getFriendList] user 不存在);return new ArrayListFriend();}// 根据 userId 查询数据库ListFriend list friendMapper.selectFriendList(user.getUserId());return list;} }用户登录后, 聊天界面会自动获取登录用户的会话列并展示 调用接口: sessionList Slf4j RestController public class MessageSessionAPI {Resourceprivate MessageSessionMapper messageSessionMapper;Resourceprivate MessageMapper messageMapper;/** 获取登录用户 的 所有会话信息 (会话id, 最后一条信息)* param req* return/RequestMapping(/sessionList)public Object getMessageSessionList(HttpServletRequest req) {ListMessageSession messageSessionList new ArrayList();// 1. 获取当前用户的 userId (从 Spring 的 session 中获取)HttpSession session req.getSession(false);if(session null) {log.info([getMessageSessionList] session null);return messageSessionList;}User user (User) session.getAttribute(user);if(user null) {log.info([getMessageSessionList] user null);return messageSessionList;}int userId user.getUserId();// 2. 根据 userId 查询数据库, 查出包含该用户的 会话 idListInteger sessionIdList messageSessionMapper.getSessionIdsByUserId(user.getUserId());//3. 遍历会话id, 查询出每个会话里涉及的好友有谁for(int sessionId : sessionIdList) {MessageSession messageSession new MessageSession();messageSession.setSessionId(sessionId);// 查询每个会话涉及的好友有谁ListFriend friends messageSessionMapper.getFriendsBySessionId(sessionId, user.getUserId());messageSession.setFriends(friends);// 查询出每个会话的最后一条消息String lastMessage messageMapper.getLastMessageBySessionId(sessionId);if (lastMessage null) {lastMessage ;}messageSession.setLastMessage(lastMessage);messageSessionList.add(messageSession);}// 最终目标是构造出一个 MessageSession 对象数组return messageSessionList;} }好友列表中, 点击某一个好友之后, 会在会话列创建出一个新会话 调用接口: session Slf4j RestController public class MessageSessionAPI {Resourceprivate MessageSessionMapper messageSessionMapper;Resourceprivate MessageMapper messageMapper;/** 创建会话, 并给会话表中插入两条信息 – 我和好友绑定的会话信息* param toUserId 好友id* param user 登录用户信息* return/TransactionalRequestMapping(/session)public Object addMessageSession(int toUserId, SessionAttribute(user) User user) {MapString, Integer resp new HashMap();// 先给 message_session 表插入数据, 获取 messageId , messageId 放在 MessionSession 对象里MessageSession messageSession new MessageSession();messageSessionMapper.addMessageSession(messageSession); //通过先插入一个空的 messageSession, 可以获取自增主键 messionId// 往 message_session_user 表里插入数据 – 自己MessageSessionUserItem item1 new MessageSessionUserItem(messageSession.getSessionId(), user.getUserId());messageSessionMapper.addMessageSessionUser(item1);// 往 message_session_user 表里插入数据 – 好友MessageSessionUserItem item2 new MessageSessionUserItem(messageSession.getSessionId(), toUserId);messageSessionMapper.addMessageSessionUser(item2);resp.put(sessionId, messageSession.getSessionId());// JSON 对于普通对象和 Map 都能处理 // return messageSession;return resp;} }会话列表中, 点击某一个会话之后, 右侧消息栏会显示出该会话的最近100条消息 调用接口: message RestController public class MessageAPI {Resourceprivate MessageMapper messageMapper;RequestMapping(/message)public Object getMessage(int sessionId) {ListMessage messages messageMapper.getMessagesBySessionId(sessionId);// 针对查询结果, 进行逆置操作Collections.reverse(messages);return messages;} }编辑消息后, 点击发送按钮会发送消息到对应会话, 该会话的所有用户的消息列表中都会出现新的消息 这里应用的 WebSocket 技术, handleTextMessage 方法能够感知到消息发送, 并获取消息信息进行处理 Slf4j Component public class WebSocketAPI extends TextWebSocketHandler {Autowiredprivate OnlineUserMapper onlineUserMapper;Autowiredprivate MessageSessionMapper messageSessionMapper;Autowiredprivate MessageMapper messageMapper;// 自己创建对象也行, 使用 Autowired 注入也行, spring 本身就有内置对象 ObjectMapperprivate ObjectMapper objectMapper new ObjectMapper();/** 数据处理* param session* param message* throws Exception/Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {log.info([WebSocketAPI] 收到消息! message.toString());// 先获取到当前用户的信息, 后续要转发的消息等User user (User) session.getAttributes().get(user);if(user null){log.info([WebSocketAPI] user null, 未登录用户, 无法进行消息转发);return;}// 针对请求进行解析, 把 json 格式字符串转换成 Java 对象MessageRequest req objectMapper.readValue(message.getPayload(), MessageRequest.class);if(message.equals(req.getType())) {// 进行消息转发transferMessage(user, req);}else {log.info([WebSocketAPI] req.type 有误! {}, message.getPayload());}}/** 通过该方法来完成消息的实际转发过程* param user 发送消息的对象* param req 内含 sessionId, content*/private void transferMessage(User user, MessageRequest req) throws IOException {// 先构造一个待转发的响应对象. MessageResponseMessageResponse resp new MessageResponse(user.getUserId(), user.getUsername(), req.getSessionId(), req.getContent());// 把这个响应对象转换成 JSON 格式字符串,以待备用String respJson objectMapper.writeValueAsString(resp);log.info([transferMessage] respJson: {}, respJson);// 根据请求中的 sessionId, 获取到 MessageSession 里有哪些用户 (查询数据库)ListFriend friends messageSessionMapper.getFriendsBySessionId(req.getSessionId(), user.getUserId());// 此处响应返回的对象中, 应该包含发送方Friend myself new Friend(user.getUserId(), user.getUsername());friends.add(myself);// 循环遍历 friends, 给其中每一个对象都发送一份响应// 这里是为了满足群聊的设定(即使前端还未实现,但是后端接口和数据库都是支持群聊的)for(Friend friend : friends) {// 已知 userId, 进一步查询 OnlineUserMapper, 获取对应的 WebSocketSession, 从而进行消息转发WebSocketSession webSocketSession onlineUserMapper.getSession(friend.getFriendId());if(webSocketSession ! null) {webSocketSession.sendMessage(new TextMessage(respJson));}}// 转发的消息还要在数据库备份Message message new Message(user.getUserId(), user.getUsername(), req.getSessionId(), resp.getContent());// 自增主键为 null或为空, 数据库会自动生成messageMapper.add(message);} }
- 上一篇: 自媒体交易网站开发网站开发与开发
- 下一篇: 自媒体网站wordpress帮朋友做网站
相关文章
-
自媒体交易网站开发网站开发与开发
自媒体交易网站开发网站开发与开发
- 技术栈
- 2026年03月21日
-
自贸区网站建设电子商务网站建设教案
自贸区网站建设电子商务网站建设教案
- 技术栈
- 2026年03月21日
-
自建站平台免费建设网站领地
自建站平台免费建设网站领地
- 技术栈
- 2026年03月21日
-
自媒体网站wordpress帮朋友做网站
自媒体网站wordpress帮朋友做网站
- 技术栈
- 2026年03月21日
-
自媒体网站程序html网页制作免费模板下载
自媒体网站程序html网页制作免费模板下载
- 技术栈
- 2026年03月21日
-
自然村 网站建设国内wordpress主题网站
自然村 网站建设国内wordpress主题网站
- 技术栈
- 2026年03月21日
