一套简单的web即时通讯——第二版
2019-05-14 17:36
483 查看
前言
接上一版,这一版的页面与功能都有所优化,具体如下:
1、优化登录拦截
2、登录后获取所有好友并区分显示在线、离线好友,好友上线、下线都有标记
3、将前后端交互的值改成用户id、显示值改成昵称nickName
4、聊天消息存储,点击好友聊天,先追加聊天记录
5、登录后获取所有未读消息并以小圆点的形式展示
6、搜索好友、添加好友
优化细节
1、登录拦截由之前的通过路径中获取账号,判断WebSocketServer.loginList中是否存在key改成登录的时候设置cookie,登录拦截从cookie中取值
登录、登出的时候设置、删除cookie,
/** * 登录 */ @PostMapping("login") public Result<ImsUserVo> login(ImsUserVo userVo, HttpServletResponse response) { //加密后再去对比密文 userVo.setPassword(MD5Util.getMD5(userVo.getPassword())); Result<List<ImsUserVo>> result = list(userVo); if (result.isFlag() && result.getData().size() > 0) { ImsUserVo imsUserVo = result.getData().get(0); //置空隐私信息 imsUserVo.setPassword(null); //add WebSocketServer.loginList WebSocketServer.loginList.put(imsUserVo.getUserName(), imsUserVo); //设置cookie Cookie cookie = new Cookie("imsLoginToken", imsUserVo.getUserName()); cookie.setMaxAge(60 * 30); //设置域 // cookie.setDomain("huanzi.cn"); //设置访问路径 cookie.setPath("/"); response.addCookie(cookie); return Result.of(imsUserVo); } else { return Result.of(null, false, "账号或密码错误!"); } } /** * 登出 */ @RequestMapping("logout/{username}") public ModelAndView loginOut(HttpServletResponse response, @PathVariable String username) { new WebSocketServer().deleteUserByUsername(username,response); return new ModelAndView("login.html"); }ImsUserController.java
改成关闭websocket时不做操作,仅减减socket连接数
/** * 连接关闭调用的方法 */ @OnClose public void onClose(Session session) { //下线用户名 String logoutUserName = ""; //从webSocketMap删除下线用户 for (Entry<String, Session> entry : sessionMap.entrySet()) { if (entry.getValue() == session) { sessionMap.remove(entry.getKey()); logoutUserName = entry.getKey(); break; } } deleteUserByUsername(logoutUserName,null); } /** 用户下线 */ public void deleteUserByUsername(String username, HttpServletResponse response){ //在线人数减减 WebSocketServer.onlineCount--; if(WebSocketServer.onlineCount <= 0){ WebSocketServer.onlineCount = 0; } if(StringUtils.isEmpty(response)){ return; } //用户集合delete WebSocketServer.loginList.remove(username); //删除cookie 思路就是替换原来的cookie,并设置它的生存时间为0 //设置cookie Cookie cookie = new Cookie("imsLoginToken", username); cookie.setMaxAge(0); //设置域 // cookie.setDomain("huanzi.cn"); //设置访问路径 cookie.setPath("/"); response.addCookie(cookie); //通知除了自己之外的所有人 sendOnlineCount(username, "{'type':'onlineCount','onlineCount':" + WebSocketServer.onlineCount + ",username:'" + username + "'}"); }WebSocketServer.java
在登录拦截器中从cookie取用户账户
//其实存的是用户账号 String imsLoginToken = ""; Cookie[] cookies = request.getCookies(); if (null != cookies) { for (Cookie cookie : cookies) { if ("imsLoginToken".equals(cookie.getName())) { imsLoginToken = cookie.getValue(); } } } if(WebSocketServer.loginList.containsKey(imsLoginToken)){ //正常处理请求 filterChain.doFilter(servletRequest, servletResponse); }else{ //重定向登录页面 response.sendRedirect("/imsUser/loginPage.html"); }LoginFilter.java
2、登录之后的用户列表不再是显示websocket连接的用户,而是登录用户的好友,同时要区分显示好友的在线与离线,所以新增一个获取在线好友的接口
/** * 获取在线好友 */ @PostMapping("getOnlineList") private Result<List<ImsUserVo>> getOnlineList(ImsFriendVo imsFriendVo) { return imsFriendService.getOnlineList(imsFriendVo); } /** * 获取在线好友 */ @Override public Result<List<ImsUserVo>> getOnlineList(ImsFriendVo imsFriendVo) { //好友列表 List<ImsFriendVo> friendList = list(imsFriendVo).getData(); //在线好友列表 ArrayList<ImsUserVo> onlineFriendList = new ArrayList<>(); //遍历friendList for(ImsFriendVo imsFriendVo1 : friendList){ ImsUserVo imsUserVo = imsFriendVo1.getUser(); if (!StringUtils.isEmpty(WebSocketServer.getSessionMap().get(imsUserVo.getId().toString()))) { onlineFriendList.add(imsUserVo); } } return Result.of(onlineFriendList); }ImsFriend
//连接成功建立的回调方法 websocket.onopen = function () { //获取好友列表 // $.post(ctx + "/imsFriend/list",{userId: username},function (data) { // console.log(data) // }); $.ajax({ type: 'post', url: ctx + "/imsFriend/list", contentType: 'application/x-www-form-urlencoded; charset=UTF-8', dataType: 'json', data: {userId: user.id}, success: function (data) { if (data.flag) { //列表 let friends = data.data; for (let i = 0; i < friends.length; i++) { let friend = friends[i].user; let $friendGroupList = $("<div class=\"hz-group-list\">" + "<img class='left' style='width: 23px;' src='https://avatars3.githubusercontent.com/u/31408183?s=40&v=4'/>" + "<span class='hz-group-list-username'>" + friend.nickName + "</span><span id=\"" + friend.id + "-status\" style='color: #9c0c0c;;'>[离线]</span>" + "<div id=\"hz-badge-" + friend.id + "\" class='hz-badge'>0</div>" + "</div>"); $friendGroupList.user = friend; $("#hz-group-body").append($friendGroupList); } //好友人数 $("#friendCount").text(friends.length); getOnlineList(user.id); } }, error: function (xhr, status, error) { console.log("ajax错误!"); } }); }; /** * 获取在线好友 */ function getOnlineList(userId){ $.ajax({ type: 'post', url: ctx + "/imsFriend/getOnlineList", contentType: 'application/x-www-form-urlencoded; charset=UTF-8', dataType: 'json', data: {userId: userId}, success: function (data) { if (data.flag) { //列表 let onlineFriends = data.data; for (let i = 0; i < onlineFriends.length; i++) { let friend = onlineFriends[i]; $("#" + friend.id + "-status").text("[在线]"); $("#" + friend.id + "-status").css("color", "#497b0f"); } //好友人数 $("#onlineCount").text(onlineFriends.length); } }, error: function (xhr, status, error) { console.log("ajax错误!"); } }); }socketChart.js
/** * 服务器接收到客户端消息时调用的方法 */ @OnMessage public void onMessage(String message, Session session) { try { //JSON字符串转 HashMap HashMap hashMap = new ObjectMapper().readValue(message, HashMap.class); //消息类型 String type = (String) hashMap.get("type"); //来源用户 Map srcUser = (Map) hashMap.get("srcUser"); //目标用户 Map tarUser = (Map) hashMap.get("tarUser"); //如果点击的是自己,那就是群聊 if (srcUser.get("userId").equals(tarUser.get("userId"))) { //群聊 groupChat(session,hashMap); } else { //私聊 privateChat(session, tarUser, hashMap); } //后期要做消息持久化 ImsFriendMessageVo imsFriendMessageVo = new ImsFriendMessageVo(); imsFriendMessageVo.setToUserId((Integer) tarUser.get("userId")); imsFriendMessageVo.setFromUserId((Integer) srcUser.get("userId")); //聊天内容 imsFriendMessageVo.setContent(hashMap.get("message").toString()); try { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); imsFriendMessageVo.setCreatedTime(simpleDateFormat.parse(hashMap.get("date").toString())); imsFriendMessageVo.setUpdataTime(simpleDateFormat.parse(hashMap.get("date").toString())); } catch (ParseException e) { e.printStackTrace(); } imsFriendMessageService.save(imsFriendMessageVo); } catch (IOException e) { e.printStackTrace(); } }WebSocketServer.java 前端点击好友时,获取聊天记录关键代码
//读取聊天记录 $.post(ctx + "/imsFriendMessage/getChattingRecords", { fromUserId: userId, toUserId: toUserId }, function (data) { if (data.flag) { for (let i = 0; i < data.data.length; i++) { let msgObj = data.data[i]; //当聊天窗口与msgUserName的人相同,文字在左边(对方/其他人),否则在右边(自己) if (msgObj.fromUserId === userId) { //追加聊天数据 setMessageInnerHTML({ id: msgObj.id, isRead: msgObj.isRead, toUserId: msgObj.toUserId, fromUserId: msgObj.fromUserId, message: msgObj.content, date: msgObj.createdTime }); } else { //追加聊天数据 setMessageInnerHTML({ id: msgObj.id, isRead: msgObj.isRead, toUserId: msgObj.fromUserId, message: msgObj.content, date: msgObj.createdTime }); } } } });socketChart.js
/** * 获取A-B的聊天记录 */ @RequestMapping("getChattingRecords") public Result<List<ImsFriendMessageVo>> getChattingRecords(ImsFriendMessageVo imsFriendMessageVo){ return imsFriendMessageService.getChattingRecords(imsFriendMessageVo); } @Override public Result<List<ImsFriendMessageVo>> getChattingRecords(ImsFriendMessageVo imsFriendMessageVo) { //A对B的聊天记录 List<ImsFriendMessageVo> allList = new ArrayList<>(super.list(imsFriendMessageVo).getData()); Integer fromUserId = imsFriendMessageVo.getFromUserId(); imsFriendMessageVo.setFromUserId(imsFriendMessageVo.getToUserId()); imsFriendMessageVo.setToUserId(fromUserId); //B对A的聊天记录 allList.addAll(super.list(imsFriendMessageVo).getData()); //默认按时间排序 allList.sort(Comparator.comparingLong(vo -> vo.getCreatedTime().getTime())); return Result.of(allList); }ImsFriendMessage
//获取未读消息 $.post(ctx + "/imsFriendMessage/list",{toUserId:userId,isRead:0},function(data){ if(data.flag){ let friends = {}; //将fromUser合并 for (let i = 0; i < data.data.length; i++) { let fromUser = data.data[i]; if(!friends[fromUser.fromUserId]){ friends[fromUser.fromUserId] = {}; friends[fromUser.fromUserId].count = 1; }else{ friends[fromUser.fromUserId].count = friends[fromUser.fromUserId].count + 1; } } for (let key in friends) { let fromUser = friends[key]; //小圆点++ $("#hz-badge-" + key).text(fromUser.count); $("#hz-badge-" + key).css("opacity", "1"); } } });socketChart.js
//搜索好友 function findUserByUserNameOrNickName() { let userNameOrNickName = $("#userNameOrNickName").val(); if (!userNameOrNickName) { tip.msg("账号/昵称不能为空"); return; } $.post(ctx + "/imsUser/findUserByUserNameOrNickName", { userName: userNameOrNickName, nickName: userNameOrNickName, }, function (data) { if (data.flag) { $("#friendList").empty(); for (let i = 0; i < data.data.length; i++) { let user = data.data[i]; let $userDiv = $("<div>" + "<img style='width: 23px;margin: 0 5px 0 0;' src='" + user.avatar + "'/>" + "<span>" + user.nickName + "(" + user.userName + ")</span>" + "<button onclick='tipUserInfo($(this).parent()[0].user)'>用户详情</button>" + "<button onclick=''>加好友</button>" + "</div>"); $userDiv[0].user = user; $("#friendList").append($userDiv); } } }); }socketChart.js
/** * 根据账号或昵称(模糊查询)查询 */ @PostMapping("findUserByUserNameOrNickName") public Result<List<ImsUserVo>> findUserByUserNameOrNickName(ImsUserVo userVo) { return imsUserService.findUserByUserNameOrNickName(userVo); } @Override public Result<List<ImsUserVo>> findUserByUserNameOrNickName(ImsUserVo userVo) { return Result.of(CopyUtil.copyList(imsUserRepository.findUserByUserNameOrNickName(userVo.getUserName(), userVo.getNickName()), ImsUserVo.class)); } @Query(value = "select * from ims_user where user_name = :userName or nick_name like %:nickName%",nativeQuery = true) List<ImsUser> findUserByUserNameOrNickName(@Param("userName") String userName,@Param("nickName") String nickName);ImsUser
/* Navicat Premium Data Transfer Source Server : localhost Source Server Type : MySQL Source Server Version : 50528 Source Host : localhost:3306 Source Schema : test Target Server Type : MySQL Target Server Version : 50528 File Encoding : 65001 Date: 14/05/2019 17:25:35 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for ims_friend -- ---------------------------- DROP TABLE IF EXISTS `ims_friend`; CREATE TABLE `ims_friend` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键', `user_id` int(11) NULL DEFAULT NULL COMMENT '用户id', `friend_id` int(11) NULL DEFAULT NULL COMMENT '好友id', `friend_type` int(11) NULL DEFAULT NULL COMMENT '好友分组id', `friend_remark` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '好友备注', `is_agree` int(1) NULL DEFAULT NULL COMMENT '是否已经同意好友申请 0已申请但未同意 1同意 -1拒绝', `created_time` datetime NULL DEFAULT NULL COMMENT '创建时间', `updata_time` datetime NULL DEFAULT NULL COMMENT '更新时间', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '好友表' ROW_FORMAT = Compact; SET FOREIGN_KEY_CHECKS = 1;View Code 在工具栏新加一个系统消息,贴出对应关键代码
//监听单击系统消息,弹出窗口 $("body").on("click", "#sysNotification", function () { //此处为单击事件要执行的代码 if ($(".sysNotification").length <= 0) { tip.dialog({ title: "系统消息", class: "sysNotification", content: "<div></div>", shade: 0 }); } else { $(".sysNotification").click(); } $("#sysNotification").find(".hz-badge").css("opacity",0); $("#sysNotification").find(".hz-badge").text(0); //已拒绝 //申请好友 $.post(ctx + "/imsFriend/list", { friendId: userId, isAgree: 0, }, function (data) { if (data.flag) { for (let i = 0; i < data.data.length; i++) { let user = data.data[i].user; let $userDiv = $("<div>" + "<img style='width: 23px;margin: 0 5px 0 0;' src='" + user.avatar + "'/>" + "<span>" + user.nickName + "(" + user.userName + ")</span> 申请添加好友<br/>" + "<button onclick='tipUserInfo($(this).parent()[0].user)'>用户详情</button>" + "<button onclick='agreeAddFriend(" + data.data[i].id + ")'>同意</button>" + "</div>"); $userDiv[0].user = user; $(".sysNotification .tip-content").append($userDiv); } } }); }); //申请添加好友 function applyToAddFriend(friendUserId) { let nowTime = commonUtil.getNowTime(); $.post(ctx + "/imsFriend/save", { userId: userId, friendId: friendUserId, friendType: 1, friendRemark: "", isAgree: 0, createdTime: nowTime, updataTime: nowTime, }, function (data) { if (data.flag) { tip.msg({text:"已为你递交好友申请,对方同意好即可成为好友!",time:3000}); } }); } //同意好友添加 function agreeAddFriend(id){ let nowTime = commonUtil.getNowTime(); $.post(ctx + "/imsFriend/save", { id:id, isAgree: 1, updataTime: nowTime, }, function (data) { if (data.flag) { $.post(ctx + "/imsFriend/save", { userId: data.data.friendId, friendId: data.data.userId, friendType: 1, friendRemark: "", isAgree: 1, createdTime: nowTime, updataTime: nowTime, }, function (data) { if (data.flag) { tip.msg({text:"你们已经是好友了,可以开始聊天!",time:2000}); } }); } }); } //获取我的申请好友,并做小圆点提示 function getApplyFriend(userId){ $.post(ctx + "/imsFriend/list", { friendId: userId, isAgree: 0, }, function (data) { if (data.flag && data.data.length > 0) { $("#sysNotification").find(".hz-badge").css("opacity",1); $("#sysNotification").find(".hz-badge").text(data.data.length); } }); }socketChart.js
在线、离线提示出来小bug...
后记
第二版暂时记录到这,第三版持续更新中...
相关文章推荐
- 简单的web三层架构系统【第二版】
- iOS webSocket之单利简单实现即时通讯
- 一套代码小程序&Web&Native运行的探索07——mpvue简单调研
- 使用Axis发布简单的Web服务
- [JavaWeb基础] 004.用JSP + SERVLET 进行简单的增加删除修改
- 简单的web备份脚本
- Python实现简单的Web服务器
- 黑客攻防技术宝典--web实战篇(第二版)人民邮电出版社
- python memcache 简单操作 (用于web前端优化,减少读库次数)
- Java 实现 web服务器的简单实例
- 简单实现web的请求派发机制
- 【持久化框架】SpringMVC+Spring4+Mybatis3集成,开发简单Web项目+源码下载
- Android-用WebView开发简单的浏览器
- 使用C++制作简单的web服务器
- webpack的最简单应用,只使用js与css的打包
- Kibana 默认Web 端口 5601 改为 80 的简单办法
- Java Web 项目简单配置 Spring MVC进行访问
- 用Web App Debugger简单调试ISAPI
- Spring Web Flow 简单实现-自定义配置文件位置
- 简单的WebView自适应并嵌套在ScrollView里