前言

  接上一版,这一版的页面与功能都有所优化,具体如下:

  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


 

  3、将之前前后端传递用户账户username改成用户id,同时,显示的是nickName昵称,改动的地方比较多,我就不贴代码了



 

   4、消息存储 

   后端存储关键代码
/** * 服务器接收到客户端消息时调用的方法 */ @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
 

 

  5、登录后获取所有未读消息并以小圆点的形式展示

  登录成功后获取与好友的未读消息关键代码,在获取好友列表之后调用
//获取未读消息 $.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


 

  6、搜索好友、添加好友

  可按照账号、昵称进行搜索,其中账号是等值查询,昵称是模糊查询

  关键代码
//搜索好友 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); } @Overridepublic
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


   添加好友

  首先要修改ims_friend结构,SQL如下,添加了一个字段is_agree,是否已经同意好友申请 0已申请但未同意 1同意
-1拒绝,之前查询好友列表的post请求则需要新增参数isAgree=1
/* 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...

 

  后记

  第二版暂时记录到这,第三版持续更新中...

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信