利用websocket实现web端在线客服实时聊天系统

利用websocket实现web端在线客服实时聊天系统websocket实现web端在线聊天

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全家桶1年46,售后保障稳定

需求场景模拟

1.移动端给客服发送消息,客户在bs端后台收到消息并回复(本文以一个客服为例)
2.左侧聊天栏显示最新的消息和消息时间
3.需要查看对方是否已读自己的消息

开发需求

一、技术选型

使用websocket进行消息推送
优点:个人感觉开发简单,不需要部署第三方服务
缺点:无状态,页面每刷新一次就会产生一个新的session,某些情况下不稳定
还是那句话,技术没有什么好与坏,只有合适不合适,同时最终采用技术栈意味着你能忍受它的缺点

二、需求分析

1.发送消息意味着需要发送人和接收人两个角色,同时需要对用户进行持久化
2.对接收人(发送人)来说,显示最新的消息和时间,就意味着显示双方消息记录的最后提条消息的内容和发送时间
3.消息已读意味着打开聊天对话框就要告诉对方,自己已读对方的消息。这里会产生两种情况:
己方在线对方未在线,需要在对方上线时(即打开对话框)告诉对方自己已读对方的消息
解决方案:存储消息数据,在自己打开对框的时候,获取聊天记录,并将聊天记录中对方给自己发的消息状态全部更新为已读。

双方同时在线(聊天对话界面),这里稍微有点操作,那就是如何让双方及时的知道对方已读自己的消息。
场景及解决方案:
场景:当自己给对方发送消息时,自己看到自己发给对方的消息已读(即便对方不给自己发消息)
解决
1.自己对对方发消息时,更新对方发给自己的全部消息为已读,对方读取消息
2.对方读取消息时,让对方告诉自己对方已读自己的消息,自己进行已读展示,通过一个共用接 口即可,当对方调用指定接口时,让对方告诉自己对方已读即可。
4.利用mongodb进行用户以及聊天记录的存储

效果演示

消息聊天演示:
请添加图片描述
消息时间演示:
在这里插入图片描述
消息未读演示:
在这里插入图片描述

软件需求实现

1.技术架构

PC端:vue+springboot
移动端:html+springboot

2.实现流程图:(仅供参考)

在这里插入图片描述

一、数据库设计

1.sys_user

字段 说明
userId 用户id
nickName 昵称
heardUrl 头像地址
sex 性别

2.chat_msg

字段 说明
createTime 创建时间
msgKey 通信key(sendId_receiveId)
chatMsg 通信消息
sendId 发送id
receiveId 接收id
readState 查看状态 0未看 1已看

二、代码实现

说明:代码有所删减修改,但核心代码存在!!!请选择性复用代码

1.web端

引入依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
            <version>2.1.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-websocket</artifactId>
            <version>9.0.39</version>
        </dependency>

Jetbrains全家桶1年46,售后保障稳定

配置yml

#开发时服务端口号,部署无效
server:
  port: 9019
  servlet: 
    context-path: fmis
spring:
  data:
    mongodb:
      host: 192.168.1.125
      port: 27017
      database: cangzhou

1.前端代码

index.vue

<template>
<div :class="classObj" class="app-wrapper">
<Logo></Logo>
<div class="clearfix"></div>
<sidebar class="sidebar-container" />
<div class="communicate" >
<el-button type="text" @click="talkRoomVisible = true" :class="{ breathing: unReadMessageNum && talkRoomVisible ==false}">
//对话图标
<img src="../../static/images/login/tips.png" alt />
</el-button>
</div>
<CommunicationInterface style=" position: absolute; z-index: 1000; top: 0; right: 0; left: 0; margin: auto; " v-if="talkRoomVisible" :userList="userList" />
</div>
</template>
<script> import message from "./mixin/message"; import CommunicationInterface from "@/dialog/chat.vue"; export default { 
 name: "Layout", components: { 
 Navbar, Sidebar, AppMain, Logo, CommunicationInterface, }, data() { 
 return { 
}; }, mixins: [ResizeMixin, message], computed: { 
 sidebar() { 
 return this.$store.state.app.sidebar; }, device() { 
 return this.$store.state.app.device; }, fixedHeader() { 
 return this.$store.state.settings.fixedHeader; }, classObj() { 
 return { 
 hideSidebar: !this.sidebar.opened, openSidebar: this.sidebar.opened, withoutAnimation: this.sidebar.withoutAnimation, mobile: this.device === "mobile", }; }, unreadList() { 
 return this.userList.filter((e) => { 
 return e.unReadMsgNum > 0; }); }, }, methods: { 
 handleClickOutside() { 
 this.$store.dispatch("app/closeSideBar", { 
 withoutAnimation: false }); }, }, }; </script>
<style lang="scss" scoped> @import "~@/styles/mixin.scss"; @import "~@/styles/variables.scss"; .breathing { 
 animation: breathe 1s infinite; } @keyframes breathe { 
 0% { 
 opacity: 0.1; } 50% { 
 opacity: 1; } 100% { 
 opacity: 0.1; } } .hoverVisibleUnreadList{ 
 position: absolute; bottom: 35px; right: -50px; width: 180px; line-height: 35px; display: none; border-radius: 3px; box-shadow: 0 0 2px #888888; } .communicate:hover .hoverVisibleUnreadList{ 
 display: block } .app-wrapper { 
 @include clearfix; position: relative; height: 100%; width: 100%; &.mobile.openSidebar { 
 position: fixed; top: 0; } } .drawer-bg { 
 background: #000; opacity: 0.3; width: 100%; top: 0; height: 100%; position: absolute; z-index: 999; } .fixed-header { 
 position: fixed; top: 0; right: 0; z-index: 9; width: calc(100% - 0); // calc(100% - #{ 
$sideBarWidth}) transition: width 0.28s; } .hideSidebar .fixed-header { 
 width: calc(100% - 0px); } .mobile .fixed-header { 
 width: 100%; } .communicate { 
 position: absolute; z-index: 2000; height: 50px; width: 50px; bottom: 50px; right: 50px; border-radius: 50%; img { 
 width: 50px; } } </style>
chat.vue
<template>
<div>
<div class="box">
<div class="min">
<ul class="contactList">
<h3 style="border-bottom: 0.5px solid #337bf1">联系人列表</h3>
<li v-for="item in userListSortByLastTime" :key="item.nickName" @click="CreateConnection(item)" :class="{ red: item.userId === userId }" >
<div class="leftLi">
<!-- <img :src="require('../../../static/images/login/titleboy.png')" /> -->
<img :src="item.heardUrl" />
<div class="ListItem">
<div>
<div class="nickName">{
{ item.nickName }}</div>
<span style="vertical-align: top">{
{
item.lastMsgTime | dateFormat
}}</span>
</div>
<div class="ellipsis">
{
{ item.lastMsg }}
<span v-if="item.unReadMsgNum > 0">{
{
[item.unReadMsgNum]
}}</span>
</div>
</div>
<div class="delete" @click="delUserByUserId(item)">删除</div>
</div>
</li>
</ul>
<div class="chat">
<h3>{
{ contactsName || "聊天区域" }}</h3>
<div class="content" ref="reference">
<ul style="color: black">
<li v-for="item in content" :key="item.id" :style="item.flag ? 'text-align: right;' : 'text-align: left;'" >
<span>
<!-- <img v-show="!item.flag" :src="require('../../../static/images/login/titleboy.png')" /> -->
<img v-show="!item.flag" :src="heardUrl" />
<span class="times" v-show="item.flag">{
{
item.createTime
? item.createTime.substr(11, item.createTime.length)
: ""
}}</span>
<span :class="item.flag ? 'i' : 'he'">{
{ item.name }}</span>
<span class="time" v-show="!item.flag">{
{
item.createTime
? item.createTime.substr(11, item.createTime.length)
: ""
}}</span>
<img v-show="item.flag" :src="require('../../../static/images/login/titleboy.png')" />
<!-- <img v-show="item.flag" :src="heardUrl" /> -->
<div class="readOrUnread" v-if="item.flag">
{
{ item.readState == "1" ? "已读" : "未读" }}
</div>
</span>
</li>
</ul>
</div>
<div class="input">
<textarea cols="50" rows="10" v-model="text" @keydown.enter="submit" ></textarea>
</div>
</div>
</div>
</div>
</div>
</template>
<script> // import axios from 'axios' import * as API from "@/api/systemApplication"; import { 
 dateFormat } from "@/utils/index"; export default { 
 props: { 
 dialogVisible: { 
 type: Boolean, default: false, }, userList: { 
 type: Array, required: true, }, }, data() { 
 return { 
 // dialogVisible: true c: null, text: null, //输入的消息 content: [], //内容列表 scoket: "", sendId: 1, userId: "", contactsName: "", URL: window.ROOT, heardUrl: "", //右侧聊天框头像 }; }, computed: { 
 userListSortByLastTime() { 
 // 做排序,最新接收到的消息放在列表的最上面,需要后端提供年月日时分秒 return this.userList.sort((a, b) => { 
 let at = a.lastMsgTime ? `${ 
a.lastMsgTime}` : "1970-01-01 00:00:00"; // console.log(at,"at") let bt = b.lastMsgTime ? `${ 
b.lastMsgTime}` : "1970-01-01 00:00:00"; // console.log(bt,"bt") return new Date(bt).getTime() - new Date(at).getTime(); }); }, }, mounted() { 
 // 因为上面用了v-if 所以每次这个组件出来 都会走mounted this.userId = this.userList[0].userId; this.heardUrl = this.userList[0].heardUrl; // 获取当前聊天人的消息 this.getSelectedUserMessageList(); // 接收ws消息 this.$root.$on("pushNewMessage", this.pushNewMessage); this.$root.$emit("changeUnReadMsgNum", { 
 userId: this.userId, }); }, destroyed() { 
 // 取消接收ws消息 this.$root.$off("pushNewMessage", this.pushNewMessage); }, filters: { 
 dateFormat(v) { 
 if (!v) { 
 return; } // 如果是昨天发的消息,左侧列表中展示的时间只显示月、日 if (v.substr(0, 10) !== dateFormat(new Date()).substr(0, 10)) { 
 return dateFormat(v, "MM-DD"); } // 如果是今天发的消息,左侧列表中展示的时间显示时、分 return dateFormat(v, "HH:mm"); }, }, methods: { 
 delUserByUserId(item) { 
 this.$root.$emit("loadUserList", { 
 userId:item.userId, }); }, // 获取历史记录 getSelectedUserMessageList() { 
 API["storeHouseChatMsgHistory"]({ 
 sendId: `${ 
this.sendId}`, receiveId: this.userId, pageSize: "100", currentPage: "1", }).then((res) => { 
 const { 
 data } = res; this.content = data.list.reverse(); this.scrollAuto(); }); }, pushNewMessage(news) { 
 if (news.code == 0) { 
 console.log(11112, news); for (var i = 0; i < this.content.length; i++) { 
 this.content[i].readState = 1; } return; } // 接收到的新消息 if (news.sendId != this.userId) return; // 这里判断id对不对再推数据 this.content.push({ 
 flag: 0, name: news.chatMsg, readState: "0", }); this.scrollAuto(); // 这里是为了做消除用户列表中的未读,也就是未读变为已读,告诉后端消息已读 API["msgRead"]({ 
 sendId: `${ 
this.sendId}`, receiveId: `${ 
this.userId}`, }); // 更改前端页面上的数据,不再提示几条未读 this.$root.$emit("changeUnReadMsgNum", { 
 userId: news.sendId, }); // if(news.code ==0){ 
 // for(var i=0;i<this.content.length;i++){ 
 // this.content[i].readState=1 // } // } }, // 按enter键(也就是要发送消息) submit() { 
 if (!this.text) { 
 return; } this.content.push({ 
 flag: 1, name: this.text, readState: "0", }); // 修改左侧列表上展示的最后一条消息和时间 this.$root.$emit("changeLast", { 
 lastMsg: this.text, lastMsgTime: dateFormat(Date.now()), userId: this.userId, }); this.$root.$talkRoomWs.send( JSON.stringify({ 
 linkType: "msg", sendId: `${ 
this.sendId}`, userId: this.sendId, receiveId: this.userId, msgKey: `${ 
this.sendId}_${ 
this.userId}`, chatMsg: this.text, readState: "0", }) ); this.text = null; this.scrollAuto(); }, // 这里是点击左侧成员列表时执行的函数 CreateConnection(item) { 
 if (this.userId === item.userId) return; this.contactsName = item.nickName; this.text = ""; this.content = []; this.userId = item.userId; this.getSelectedUserMessageList(); this.$root.$emit("changeUnReadMsgNum", item); API["msgRead"]({ 
 sendId: `${ 
this.sendId}`, receiveId: `${ 
this.userId}`, }); // 点击左侧列表时,聊天框中的头像也要切换 for (var i = 0; i < this.userList.length; i++) { 
 if (this.userList[i].userId == item.userId) { 
 this.heardUrl = this.userList[i].heardUrl; } } }, scrollAuto() { 
 this.$nextTick(() => { 
 this.$refs["reference"].scrollTop = this.$refs["reference"].scrollHeight; }); }, }, }; </script>
<style lang="scss" scoped> h3 { 
 text-align: center; height: 40px; line-height: 40px; border-bottom: 0.5px solid #ccc; font-size: 16px; font-weight: 300; } .box { 
 margin-top: 150px; margin-left: 28%; width: 900px; border-radius: 10px; overflow: hidden; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04); .min { 
 display: flex; .contactList { 
 list-style: none; overflow: auto; height: 600px; width: 260px; background: #4387f6; color: azure; padding: 0px 5px; padding-left: 5px; .red { 
 background: #5d9aff; border-radius: 10px; } li { 
 cursor: pointer; text-align: left; box-sizing: border-box; padding: 14px 0px; padding-left: 5px; height: 65px; } .leftLi { 
 @extend .min; img { 
 height: 40px; display: block; } .ListItem { 
 @extend .min; padding-left: 5px; flex-direction: column; } div :first-child { 
 height: 30px; line-height: 20px; span { 
 color: #bed7ff; font-size: 12px; margin-left: 15px; } } div :last-child { 
 line-height: 0px; color: #bed7ff; font-size: 12px; span { 
 text-align: center; border-radius: 3px; } } } } .chat { 
 background: #f3f3f3; width: 700px; height: 600px; @extend .min; flex-direction: column; .content { 
 overflow: auto; height: 500px; span { 
 display: inline-block; padding: 0px 5px; line-height: 28px; @mixin name($px) { 
 transition: 0.5s; opacity: 0; transform: translate($px); } .time { 
 @include name(-20px); } .times { 
 @include name(20px); } } span:hover .time { 
 opacity: 1; transform: translate(0px); } span:hover .times { 
 opacity: 1; transform: translate(0px); } @mixin Bubble($color) { 
 background: $color; border-radius: 5px; border-bottom-left-radius: 20px; } .i { 
 color: #f3f3f3; @include Bubble(#5d9aff); max-width: 65%; text-align: left; word-wrap: break-word; word-break: break-all; overflow: hidden; padding: 8px 5px; } .he { 
 @include Bubble(#e9e9e9); max-width: 65%; word-wrap: break-word; word-break: break-all; overflow: hidden; padding: 8px 5px; } } .input { 
 height: 180px; border-top: 0.5px solid #f3f3f3; textarea { 
 width: 100%; border: 0px; resize: none; } } } } } ul > li { 
 width: 100px; text-align: center; width: 100%; margin-top: 3px; } img { 
 height: 45px; vertical-align: top; border-radius: 50%; } .content li > span { 
 position: relative; } .content li { 
 margin-top: 15px; } .readOrUnread { 
 font-size: 12px; margin-right: 47px; line-height: 12px; } .ellipsis { 
 width: 100px; height: 12px; line-height: 12px !important; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .nickName { 
 display: inline-block; width: 90px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .leftLi { 
 position: relative; } .delete { 
 position: absolute; bottom: 3px; right: 45px; color: #fff; } </style>

message.js

import * as API from "@/api/systemApplication";
export default { 

data() { 

return { 

sendId: 1, // todo 客服id,默认为1
scoket: null,
userList: [], //联系人列表
talkRoomVisible: false,
}
},
computed: { 

// 未读消息的数量
unReadMessageNum() { 

let unReadNum = 0;
for (let i = 0; i < this.userList.length; i++) { 

const e = this.userList[i];
// 注意这里的unReadMsgNum可能不是数字
if (e.unReadMsgNum && typeof e.unReadMsgNum == 'number') { 

unReadNum += e.unReadMsgNum
}
}
return unReadNum
}
},
mounted() { 

// 初始化ws
this.initWs();
// 获取左侧用户列表
this.getList();
// 如果聊天窗口是开启状态,按esc键,则关闭聊天窗口
this.initCloseTalkRoom()
// 修改前端页面上左侧列表展示的未读条数
this.$root.$on("changeUnReadMsgNum", this.changeUnReadMsgNum);
// 修改左侧列表上展示的最后一条消息和时间
this.$root.$on("changeLast", this.changeLast);
// 删除聊天室左侧列表中的项,再重新加载用户列表
this.$root.$on("loadUserList", this.loadUserList);
},
methods: { 

initWs() { 

// let url = this.URL;
// this.$root.$talkRoomWs = new WebSocket("ws://192.168.1.169:9019/fmis-api/ws/asset");
let url = window.ROOT;
this.$root.$talkRoomWs =  new WebSocket(`${ 
url.replace("http", "ws")}/ws/asset`);
let ws = this.$root.$talkRoomWs;
ws.onopen = (e) => { 

console.log(`WebSocket 连接状态: ${ 
ws.readyState}`);
};
ws.onmessage = (data) => { 

try { 

let news = JSON.parse(data.data)
// 用新信息覆盖旧消息
if (news.unReadNumLst) { 

for (let i = 0; i < news.unReadNumLst.length; i++) { 

const e = news.unReadNumLst[i];
let index = this.userList.findIndex(item => item.userId == e.userId)
if (index > -1) { 

// this.userList[index].lastMsg = e.lastMsg
// this.userList[index].lastMsgTime = e.lastMsgTime
// this.userList[index].unReadMsgNum = e.unReadMsgNum
Object.assign(this.userList[index], e)
} else { 

// todo 新增的逻辑
this.userList.push(e)
}
}
}
this.$root.$emit('pushNewMessage', news)
} catch (err) { 

// console.log(err);
}
};
ws.onclose = (data) => { 

console.log("WebSocket连接已关闭");
};
setTimeout(() => { 

ws.send(
JSON.stringify({ 

linkType: "bind",
sendId: this.sendId,
})
);
}, 500);
},
// 获取左侧的用户列表
getList() { 

API["storeHouseWsUserList"]({ 

sendId: this.sendId,
userId: this.sendId,
}).then((res) => { 

const { 
 data } = res;
this.userList = data;
});
},
// 如果聊天窗口是开启状态,按esc键,则关闭聊天窗口
initCloseTalkRoom() { 

window["onkeydown"] = (e) => { 

if (e.keyCode === 27 && this.talkRoomVisible) { 

this.talkRoomVisible = false
}
};
},
// 修改前端页面上左侧列表展示的未读条数
changeUnReadMsgNum(item) { 

let index = this.userList.findIndex((e) => e.userId == item.userId);
console.log(index);
if (index > -1) { 

this.userList[index].unReadMsgNum = 0;
}
console.log(this.userList[index].unReadMsgNum);
},
// 修改左侧列表上展示的最后一条消息和时间
changeLast(obj) { 

this.userList.find((item) => { 

if (item.userId === obj.userId) { 

item.lastMsg = obj.lastMsg;
item.lastMsgTime = obj.lastMsgTime;
}
});
},
loadUserList(item) { 

console.log(item,"item")
let postData= { 

userId:item.userId
}
API["delUserByUserId"](postData).then((res) => { 

console.log(res);
if (res.code) { 

this.$message({ 

message: res.message,
type: "success",
});
this.getList()
}
});
}
}
}

systemApplication.js


// 获取列表
export function storeHouseWsUserList(data) { 

return request({ 

url: '/chat/userList',
method: 'post',
data
})
}
// 获取历史连天记录
export function storeHouseChatMsgHistory(data) { 

return request({ 

url: '/chat/msgHistory',
method: 'post',
data
})
}
// 已读未读
export function msgRead(data) { 

return request({ 

url: '/chat/msgRead',
method: 'post',
data
})
}
// 删除左侧列表中的项
export function delUserByUserId(data) { 

return request({ 

url: '/chat/delUserByUserId',
method: 'post',
data
})
}

2.后端代码

WebSocketServer

import com.alibaba.fastjson.JSONObject;
import com.jinmdz.fmis.api.api.model.storehouse.chat.ChatMsg;
import com.jinmdz.fmis.api.global.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
/** * @author lvyq * @version 1.0 * @description: TODO * @date 2021/8/17 16:30 */
@ServerEndpoint(value = "/ws/asset")
@Component
public class WebSocketServer { 

private static ChatService  chatService;
@Autowired
public  void setChatService(ChatService chatService) { 

WebSocketServer.chatService = chatService;
}
@PostConstruct
public void init() { 

System.out.println("websocket 加载");
}
private static Logger log = LoggerFactory.getLogger(WebSocketServer.class);
private static final AtomicInteger OnlineCount = new AtomicInteger(0);
// concurrent包的线程安全Set,用来存放每个客户端对应的Session对象。
private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>();
/** * 连接建立成功调用的方法 */
@OnOpen
public void onOpen(Session session) { 

SessionSet.add(session);
int cnt = OnlineCount.incrementAndGet();
log.info("有连接加入,当前连接数为:{}", cnt);
SendMessage(session, "连接成功");
}
/** * 连接关闭调用的方法 */
@OnClose
public void onClose(Session session) { 

SessionSet.remove(session);
int cnt = OnlineCount.decrementAndGet();
log.info("有连接关闭,当前连接数为:{}", cnt);
}
/** * 收到客户端消息后调用的方法 * @param message 客户端发送过来的消息 */
@OnMessage
public void onMessage(String message, Session session) { 

log.info("来自客户端的消息:{}",message);
try{ 

JSONObject jsonObject = JSONObject.parseObject(message);
String linkType = jsonObject.getString("linkType");
String sendId = jsonObject.getString("sendId");
if (linkType.equals("bind")){ 

CacheManager.set("bind_"+sendId,session);
SendMessage(session, "连接成功");
}else if (linkType.equals("msg")){ 

//聊天
ChatMsg msg = new ChatMsg();
//发消息
String chatMsg = jsonObject.getString("chatMsg");
String receiveId = jsonObject.getString("receiveId");
msg.setChatMsg(chatMsg);
msg.setSendId(sendId);
msg.setMsgKey(sendId+"_"+receiveId);
msg.setReceiveId(receiveId);
msg.setReadState("0");
chatService.sendOne(msg);}
}catch (Exception e){ 

e.printStackTrace();
}
}
/** * 出现错误 * @param session * @param error */
@OnError
public void onError(Session session, Throwable error) { 

log.error("发生错误:{},Session ID: {}",error.getMessage(),session.getId());
error.printStackTrace();
}
/** * 发送消息,实践表明,每次浏览器刷新,session会发生变化。 * @param session * @param message */
public static void SendMessage(Session session, String message) { 

try { 

//session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId()));
log.info("sessionID="+session.getId());
session.getBasicRemote().sendText(message);
} catch (IOException e) { 

log.error("发送消息出错:{}", e.getMessage());
e.printStackTrace();
}
}
/** * 指定Session发送消息 * @param sessionId * @param message * @throws IOException */
public static void SendMessageById(String message,String sessionId) throws IOException { 

Session session = null;
for (Session s : SessionSet) { 

if(s.getId().equals(sessionId)){ 

session = s;
break;
}
}
if(session!=null){ 

SendMessage(session, message);
}
else{ 

log.warn("没有找到你指定ID的会话:{}",sessionId);
}
}
/** * @description: 指定发送 * @author: lvyq * @date: 2021/9/24 11:30 * @version 1.0 */
public static void SendMessageByRecId(String message,String receiveId) throws IOException { 

Session session=  CacheManager.get("bind_"+receiveId);
String sessionId = "";
if (session!=null){ 

sessionId=session.getId();
}
for (Session s : SessionSet) { 

if(s.getId().equals(sessionId)){ 

session = s;
break;
}
}
if(session!=null){ 

SendMessage(session, message);
}
else{ 

log.warn("没有找到你指定ID的会话:{}",sessionId);
}
}

ChatController

import com.jinmdz.fmis.api.api.model.storehouse.chat.ChatMsg;
import com.jinmdz.fmis.api.api.model.storehouse.chat.SysUser;
import com.jinmdz.fmis.api.api.service.ChatService;
import com.jinmdz.fmis.api.api.service.WebSocketServer;
import com.jinmdz.fmis.api.base.BaseController;
import com.jinmdz.fmis.core.base.BaseResult;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.io.IOException;
/** * @author lvyq * @version 1.0 * @description: TODO-消息通信 * @date 2021/9/26 14:50 */
@RestController
@RequestMapping("/chat")
public class ChatController extends BaseController { 

@Resource
private ChatService chatService;
@Resource
private WebSocketServer webSocketServer;
/** * @description: 消息记录 * @author: lvyq * @date: 2021/9/26 15:19 * @version 1.0 */
@PostMapping("/msgHistory")
public BaseResult msgHistory(@RequestBody ChatMsg data) throws IOException { 

return  chatService.msgHistory(data);
}
/** * @description: 消息已读 * @author: lvyq * @date: 2021/9/26 16:27 * @version 1.0 */
@PostMapping("/msgRead")
private BaseResult msgRead(@RequestBody ChatMsg data) throws IOException { 

return  chatService.msgRead(data);
}
/** * @description: 全体发送 * @author: lvyq * @date: 2021/11/11 13:38 * @version 1.0 */
@GetMapping("/sendAll/{msg}")
public void sendAll(@PathVariable("msg") String msg) throws IOException { 

WebSocketServer.BroadCastInfo(msg);
}
/** * @description: 发消息给某人 * @author: lvyq * @date: 2021/9/24 11:15 * @version 1.0 */
@PostMapping("/sendOne")
public void sendOne(@RequestBody ChatMsg data) throws IOException { 

chatService.sendOne(data);
}
/** * @description: 获取用户列表 * @author: lvyq * @date: 2021/9/24 13:43 * @version 1.0 */
@PostMapping("/userList")
private BaseResult userList(@RequestBody SysUser data){ 

return chatService.userListDev(data);
}
/** * @description: 根据userId删除用户 * @author: lvyq * @date: 2021/11/19 13:29 * @version 1.0 */
@PostMapping("/delUserByUserId")
public BaseResult delUserByUserId(@RequestBody SysUser data){ 

return chatService.delUserByUserId(data.getUserId());
}
}

ChatService

import com.alibaba.fastjson.JSONObject;
import com.jinmdz.fmis.api.api.model.storehouse.chat.ChatMsg;
import com.jinmdz.fmis.api.api.model.storehouse.chat.SysUser;
import com.jinmdz.fmis.api.api.service.repository.SysUserRepository;
import com.jinmdz.fmis.api.base.BaseService;
import com.jinmdz.fmis.api.model.system.UserItem;
import com.jinmdz.fmis.core.base.BaseResult;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** * @author lvyq * @version 1.0 * @description: TODO * @date 2021/9/24 10:06 */
@Service
public class ChatService extends BaseService { 

@Resource
private MongoTemplate mongoTemplate;
/** * @description: 获取用户列表 * @author: lvyq * @date: 2021/9/24 10:12 * @version 1.0 */
public BaseResult userList(UserItem userItem, SysUser data) { 

String receiveId = userItem.getId().toString();
List<SysUser> sysUserList = userLst(receiveId);
return successData(sysUserList);
}
/** * @description: 发消息给某人 * @author: lvyq * @date: 2021/9/24 11:16 * @version 1.0 */
public void sendOne(ChatMsg data) throws IOException { 

mongoTemplate.save(data);
//TODO 更新消息 接收者_发送者
String msg_key = data.getReceiveId()+"_"+data.getSendId();
String receiveId = data.getReceiveId();
HashMap<String,Object> map = new HashMap<>();
JSONObject mav = new JSONObject();
Query queryMsg = new Query().addCriteria(Criteria.where("readState").is("0").and("msgKey").is(msg_key));
Update update = new Update().set("readState","1");
//批量修改
mongoTemplate.updateMulti(queryMsg,update,ChatMsg.class,"chat_msg");
mav.put("sendId",data.getSendId());
mav.put("chatMsg",data.getChatMsg());
mav.put("unReadNumLst",userLst(receiveId));
WebSocketServer.SendMessageByRecId(mav.toJSONString(),receiveId);
}
/** * @description: 消息列表信息-unLogin * @author: lvyq * @date: 2021/9/26 17:50 * @version 1.0 */
public BaseResult userListDev(SysUser data) { 

String receiveId = data.getUserId();
List<SysUser> sysUserList = userLst(receiveId);
return successData(sysUserList);
}
/** * @description: 消息记录 * @author: lvyq * @date: 2021/9/26 15:20 * @version 1.0 */
public BaseResult msgHistory(ChatMsg data) throws IOException { 

int page = data.getCurrentPage();
if (page<0){ 

page=1;
}
page=page-1;
int size = data.getPageSize();
if (size<0){ 

size=20;
}
if (StringUtils.isNotEmpty(data.getSendId()) && StringUtils.isNotEmpty(data.getReceiveId())){ 

String msgKeyOne= data.getSendId()+"_"+data.getReceiveId();
String msgKeyTwo = data.getReceiveId()+"_"+data.getSendId();
Criteria orCri1 = new Criteria();
Criteria orCri2 = new Criteria();
orCri1.andOperator(Criteria.where("msgKey").is(msgKeyOne));
orCri2.andOperator(Criteria.where("msgKey").is(msgKeyTwo));
Query query = new Query().addCriteria(new Criteria().orOperator(orCri1,orCri2));
long total = mongoTemplate.count(query,ChatMsg.class);
Pageable pageable = PageRequest.of(page,size, Sort.by(Sort.Direction.DESC,"createTime"));
query.with(pageable);
List<ChatMsg> list = mongoTemplate.find(query,ChatMsg.class);
HashMap<String, Object> map = new HashMap<>();
if (list != null) { 

for (ChatMsg chatMsg:list){ 

chatMsg.setName(chatMsg.getChatMsg());
if (chatMsg.getSendId().equals(data.getSendId())){ 

chatMsg.setFlag(1);
}else { 

chatMsg.setFlag(0);
}
}
Map<String, Object> pageMap = new HashMap<>();
map.put("list", list);
pageMap.put("total", total);
pageMap.put("pageSize", data.getPageSize());
pageMap.put("currentPage", data.getCurrentPage());
map.put("pager", pageMap);
}
Query queryMsg = new Query().addCriteria(Criteria.where("readState").is("0").and("sendId").is(data.getReceiveId()).and("receiveId").is(data.getSendId()));
Update update = new Update().set("readState","1");
mongoTemplate.updateMulti(queryMsg,update,ChatMsg.class,"chat_msg");
//消息已读通知
/*JSONObject mav = new JSONObject(); mav.put("state",true); mav.put("msg","消息已读"); WebSocketServer.SendMessageByRecId(mav.toJSONString(),data.getReceiveId());*/
return successData(map);
}else { 

return failure("非法参数");
}
}
/** * @description: 将消息修改为已读 * @author: lvyq * @date: 2021/9/26 16:27 * @version 1.0 */
public BaseResult msgRead(ChatMsg data) throws IOException { 

Query query = new Query().addCriteria(Criteria.where("readState").is("0").and("sendId").is(data.getReceiveId()).and("receiveId").is(data.getSendId()));
Update update = new Update().set("readState","1");
mongoTemplate.updateMulti(query,update,ChatMsg.class,"chat_msg");
//消息已读通知
JSONObject mav = new JSONObject();
mav.put("state",true);
mav.put("msg","消息已读");
mav.put("code",0);
WebSocketServer.SendMessageByRecId(mav.toJSONString(),data.getReceiveId());
return success("操作成功");
}
/** * @description: 获取集合 * @author: lvyq * @date: 2021/9/26 17:46 * @version 1.0 */
private  List<SysUser> userLst(String receiveId){ 

List<SysUser> sysUserList = mongoTemplate.findAll(SysUser.class);
for (SysUser sysUser:sysUserList){ 

String sendId = sysUser.getUserId();
String msgKey = sendId+"_"+receiveId;
if (sendId.equals(receiveId)){ 

continue;
}
//最新一条记录
String msgKeyOne= sendId+"_"+receiveId;
String msgKeyTwo = receiveId+"_"+sendId;
Criteria orCri1 = new Criteria();
Criteria orCri2 = new Criteria();
orCri1.andOperator(Criteria.where("msgKey").is(msgKeyOne));
orCri2.andOperator(Criteria.where("msgKey").is(msgKeyTwo));
Query msgQuery = new Query().addCriteria(new Criteria().orOperator(orCri1,orCri2));
Pageable pageable = PageRequest.of(0,1, Sort.by(Sort.Direction.DESC,"createTime"));
msgQuery.with(pageable);
List<ChatMsg> list = mongoTemplate.find(msgQuery,ChatMsg.class);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
if (list.size()>0){ 

sysUser.setLastMsg(list.get(0).getChatMsg());
sysUser.setLastMsgTime(simpleDateFormat.format(list.get(0).getCreateTime()));
}else { 

sysUser.setLastMsg("");
sysUser.setLastMsgTime("");
}
Query query = new Query().addCriteria(Criteria.where("readState").is("0").and("msgKey").is(msgKey));
long unReadNum = mongoTemplate.count(query, ChatMsg.class);
sysUser.setUnReadMsgNum(unReadNum);
}
return sysUserList;
}
/** * @description:时间转换 * @author: lvyq * @date: 2021/9/27 16:48 * @version 1.0 */
private String changeTime(Date createTime) { 

Date date = new Date();
SimpleDateFormat formatDay = new SimpleDateFormat("MM-dd");
if (formatDay.format(date).equals(formatDay.format(createTime))){ 

SimpleDateFormat formatMin = new SimpleDateFormat("HH:mm");
return formatMin.format(createTime);
}else { 

return formatDay.format(createTime);
}
}
/** * @description: 根据userId删除用户 * @author: lvyq * @date: 2021/11/19 13:29 * @version 1.0 */
public BaseResult delUserByUserId(String userId) { 

Query query = new Query().addCriteria(Criteria.where("userId").is(userId));
mongoTemplate.findAndRemove(query,SysUser.class);
return success("操作成功");
}
}

ChatMsg

import com.fasterxml.jackson.annotation.JsonFormat;
import com.jinmdz.fmis.core.base.BasePageData;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Date;
/** * @author lvyq * @version 1.0 * @description: 消息通信 * @date 2021/9/24 9:00 */
@Data
@Document("chat_msg")
public class ChatMsg extends BasePageData { 

@Id
private String id;
/** * 创建时间 */
@JsonFormat(pattern = yyyy_MM_dd_HH_mm_ss)
private Date createTime=new Date();
/** * 通信key(sendId_receiveId) */
private String msgKey;
/** * 通信消息 */
private String chatMsg;
/** * 发送id */
private String sendId;
/** * 接收id */
private String receiveId;
/** * 查看状态 0未看 1已看 */
private String readState;
/** *1为我 0 为他 */
@Transient
private Integer flag;
@Transient
private String  name;
}

SysUser

import com.jinmdz.fmis.core.base.BasePageData;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;
/** * @author lvyq * @version 1.0 * @description: TODO * @date 2021/9/24 9:14 */
@Data
@Document("sys_user")
public class SysUser extends BasePageData { 

@Id
private String id;
/** * 用户id */
private String userId;
/** * 昵称 */
private String nickName;
/** * 头像地址 */
private String heardUrl;
/** * 性别 */
private Integer sex;
/** * 登录状态 0未登录 1已登录 */
private Integer loginState;
/** * 用户发给自己且未读的消息数 */
@Transient
private long unReadMsgNum;
/** * 最新一条消息 */
@Transient
private String lastMsg;
/** * 最新一条消息时间 */
@Transient
private String lastMsgTime;
}

2.移动端

1.前端代码:

说明:userid在登录系统时存储在了cookie中

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0">
<title>问题解答</title>
<script src="../../common/js/GBCookie.js"></script>
<style type="text/css"> body { 
 overflow: hidden; } h3 { 
 text-align: center; height: 40px; line-height: 40px; border-bottom: 0.5px solid #ccc; font-size: 16px; font-weight: 300; } .chat { 
 background: #f3f3f3; /*width: 700px;*/ /*height: 600px;*/ width: 100vw; height: 100vh; } .chat > h3 { 
 height: 5vh; line-height: 5vh; } .content { 
 overflow: auto; /*height: 500px;*/ height: 75vh; } ul { 
 list-style: none; } ul > li { 
 width: 100px; text-align: center; line-height: 50px; width: 100%; margin-top: 3px; } img { 
 height: 45px; vertical-align: bottom; border-radius: 50%; } .input { 
 height: 180px; border-top: 0.5px solid #f3f3f3; } textarea { 
 width: 100%; border: 0px; resize: none; height: 20vh; } span { 
 display: inline-block; padding: 0px 5px; height: 37px; /*line-height: 37px;*/ line-height: 30px; } span:hover .times { 
 opacity: 1; transform: translate(0px); } span:hover .time { 
 opacity: 1; transform: translate(0px); } .times { 
 -webkit-transition: 0.5s; transition: 0.5s; opacity: 0; -webkit-transform: translate(20px); transform: translate(20px); } .time { 
 -webkit-transition: 0.5s; transition: 0.5s; opacity: 0; -webkit-transform: translate(-20px); transform: translate(-20px); } .he { 
 background: #e9e9e9; border-radius: 5px; border-bottom-right-radius: 15px; } .i { 
 color: #f3f3f3; background: #5d9aff; border-radius: 5px; border-bottom-left-radius: 14px; } .state { 
 font-size: 12px; } .readOrUnread { 
 /*position: absolute;*/ /*right: 55px;*/ /*bottom: -39px;*/ /*font-size: 12px;*/ margin-right: 55px; } .content li > span { 
 position: relative; } .content li { 
 margin-top: 15px; } li .i, li .he { 
 max-width: 50vw; word-wrap: break-word; word-break: break-all; text-align: left; height: auto; padding: 7px 5px; } </style>
<script src="../../common/vue/vue.min.js"></script>
<script src="../../common/axios/axios.min.js"></script>
<link rel="stylesheet" href="../../common/css/base.css">
</head>
<body>
<div id="app">
<div class="chat"><h3>客服<span class="state"></span></h3>
<div class="content" ref="reference">
<ul style="color: black">
<li v-for="item in content" :key="item.id" :style="item.flag ? 'text-align: right;' : 'text-align: left;'">
<span>
<img v-show="!item.flag" src="../img/icon/01.png" />
<span class="times" v-show="item.flag">{
{
item.createTime
? item.createTime.substr(11, item.createTime.length)
: ""
}}</span>
<span :class="item.flag ? 'i' : 'he'">{
{ item.name }}</span>
<span class="time" v-show="!item.flag">{
{
item.createTime
? item.createTime.substr(11, item.createTime.length)
: ""}}
</span>
<img v-show="item.flag" :src="heardUrl" />
<div class="readOrUnread" v-if="item.flag">
{
{ item.readState == "1" ? "已读" : "未读" }}
</div>
</span>
</li>
</ul>
</div>
<div class="input">
<textarea cols="50" rows="10" v-model="text" @keydown.enter="submit" ></textarea>
</div>
</div>
</div>
<script type="text/javascript"> var app = new Vue({ 
 el: "#app", data() { 
 return { 
 text: "", content: [], socket: "", userId: "", openId: "", heardUrl: "", webUrl: "" } }, created() { 
 this.openId = getCookie('openId'); }, mounted() { 
 this.getWebUrl() this.getSysUserInfoByUserId() }, methods: { 
 submit() { 
 if (!this.text) { 
 return; } this.content.push({ 
 flag: 1, name: this.text, }); this.socket.send( JSON.stringify({ 
 linkType: "msg", sendId: this.openId, userId: this.openId, receiveId: 1, msgKey: this.openId + '_1', chatMsg: this.text, readState: 0, }) ); this.text = null; this.scrollAuto(); }, scrollAuto() { 
 this.$nextTick(() => { 
 this.$refs["reference"].scrollTop = this.$refs["reference"].scrollHeight }); }, history() { 
 axios({ 
 method: "post", url: "../../../chat/msgHistory", data: { 
 receiveId: 1, sendId: this.openId, pageSize: "20", currentPage: "1", } }).then((res) => { 
 if (res) { 
 const { 
data} = res; this.content = data.data.list.reverse(); this.scrollAuto(); } }) this.msgRead() }, msgRead() { 
 let url = `http://${ 
this.webUrl}/chat/msgRead` console.log(url, "url") axios({ 
 method: "post", url: url, data: { 
 sendId: this.openId, receiveId: `1`, } }) }, getWebUrl() { 
 axios({ 
 method: "POST", url: "../../../chat/getWebUrl", }).then((res) => { 
 if (res) { 
 const { 
data} = res this.webUrl = data.webUrl this.history() this.initWs() } }) }, initWs() { 
 if (typeof (WebSocket) == "undefined") { 
 console.log("遗憾:您的浏览器不支持WebSocket"); } else { 
 console.log("恭喜:您的浏览器支持WebSocket"); this.socket = new WebSocket(`ws://${ 
this.webUrl}/ws/asset`); //连接打开事件 this.socket.onopen = function () { 
 console.log("Socket 已打开"); //session与用户id绑定 this.socket.send("{\"linkType\":\"bind\",\"sendId\":\""${ 
this.sendId}"\"} "); }; //收到消息事件 this.socket.onmessage = (data) => { 
 console.log(data) if (data.data !== '连接成功') { 
 let news = JSON.parse(data.data); this.content.push({ 
 flag: 0, name: news.chatMsg, }); //对方接收到消息之后,把未读改为已读 if (news.code == 0) { 
 this.content.pop() for (var i = 0; i < this.content.length; i++) { 
 this.content[i].readState = 1 } } this.msgRead() this.scrollAuto(); } }; // socket. //连接关闭事件 this.socket.onclose = function () { 
 console.log("Socket已关闭"); }; //发生了错误事件 this.socket.onerror = function () { 
 alert("Socket发生了错误"); } //窗口关闭时,关闭连接 window.unload = function () { 
 this.socket.close(); }; } setTimeout(() => { 
 this.socket.send( JSON.stringify({ 
 linkType: "bind", sendId: this.openId, }) ); }, 500); }, //通过openId获取用户头像信息 getSysUserInfoByUserId() { 
 axios({ 
 method: "GET", url: "../../../chat/getSysUserInfoByUserId", params: { 
 "openId": this.openId } }).then((res) => { 
 if (res.data.success) { 
 if (res.data.data) { 
 this.heardUrl = res.data.data.heardUrl console.log(this.heardUrl) } } }) } } }) </script>
</body>
</html>

GBCookie.js

function setCookie(c_name, value, expiredays) { 

var exdate = new Date()
exdate.setDate(exdate.getDate() + expiredays)
document.cookie = c_name + "=" + escape(value) +
((expiredays == null) ? "" : ";expires=" + exdate.toGMTString())
}
//取回cookie
function getCookie(c_name) { 

if (document.cookie.length > 0) { 

c_start = document.cookie.indexOf(c_name + "=")`在这里插入代码片`
if (c_start != -1) { 

c_start = c_start + c_name.length + 1
c_end = document.cookie.indexOf(";", c_start)
if (c_end == -1) c_end = document.cookie.length
return unescape(document.cookie.substring(c_start, c_end))
}
}
return ""
}

2.后端代码

引入mongodb依赖

        <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>

yml配置

#mongodb 配置
spring:
data:
mongodb:
host: 192.168.1.125
port: 27017
database: cangzhou
# 客服消息地址
websocket:
url: 192.168.1.209:9019/fmis

WxChatController

import com.alibaba.fastjson.JSONObject;
import com.jinmdz.common.response.CommonCode;
import com.jinmdz.entity.wechat.SysUser;
import com.jinmdz.entity.wechat.WeChatResult;
import com.jinmdz.model.chat.ChatMsg;
import com.jinmdz.service.ChatService;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/** * @author lvyq * @version 1.0 * @description: TODO * @date 2021/11/12 13:18 */
@RestController
@RequestMapping("/chat")
public class WxChatController { 

@Resource
public MongoTemplate mongoTemplate;
@Resource
private ChatService chatService;
/** * @description: 添加用户-测试 * @author: lvyq * @date: 2021/11/18 16:08 * @version 1.0 */
@PostMapping("/saveUser")
public WeChatResult saveUser(@RequestBody SysUser user){ 

Query query = new Query().addCriteria(Criteria.where("userId").is(user.getUserId()));
Update update = new Update().set("nickName",user.getNickName()).set("heardUrl",user.getHeardUrl());
SysUser sysUser=  mongoTemplate.findOne(query,SysUser.class);
if (sysUser!=null){ 

mongoTemplate.updateFirst(query,update,SysUser.class);
}else { 

mongoTemplate.insert(user);
}
return new WeChatResult(CommonCode.SUCCESS);
}
/** * @description: 消息记录 * @author: lvyq * @date: 2021/11/12 13:18 * @version 1.0 */
@PostMapping("/msgHistory")
public JSONObject msgHistory(@RequestBody ChatMsg data){ 

return  chatService.msgHistory(data);
}
/** * @description: 获取ws的地址 * @author: lvyq * @date: 2021/11/16 14:45 * @version 1.0 */
@PostMapping("/getWebUrl")
public JSONObject getWebUrl(){ 

return chatService.getWebUrl();
}
/** * @description: 根据用户openId获取用户基本信息 * @author: lvyq * @date: 2021/11/18 16:27 * @version 1.0 */
@GetMapping("/getSysUserInfoByUserId")
public WeChatResult getSysUserInfoByUserId(String openId){ 

if (StringUtils.isEmpty(openId)){ 

return new WeChatResult(CommonCode.INVALID_PARAM);
}
Query query = new Query().addCriteria(Criteria.where("userId").is(openId));
SysUser sysUser = mongoTemplate.findOne(query,SysUser.class);
return new WeChatResult(sysUser);
}
}

ChatService

import com.alibaba.fastjson.JSONObject;
import com.jinmdz.model.chat.ChatMsg;
/** * @author lvyq * @version 1.0 * @description: TODO * @date 2021/11/12 13:18 */
public interface ChatService { 

JSONObject msgHistory(ChatMsg data);
JSONObject getUnReadNum(ChatMsg data);
JSONObject getWebUrl();
}

ChatServiceImpl

import com.alibaba.fastjson.JSONObject;
import com.jinmdz.model.chat.ChatMsg;
import com.jinmdz.service.ChatService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** * @author lvyq * @version 1.0 * @description: TODO * @date 2021/11/12 13:19 */
@Service
public class ChatServiceImpl implements ChatService { 

@Resource
private MongoTemplate mongoTemplate;
@Value("${websocket.url}")
public  String WebUrl;
/** * @description: 消息记录 * @author: lvyq * @date: 2021/9/26 15:20 * @version 1.0 */
@Override
public JSONObject msgHistory(ChatMsg data) { 

JSONObject mav = new JSONObject();
int page = data.getCurrentPage();
if (page<0){ 

page=1;
}
page=page-1;
int size = data.getPageSize();
if (size<0){ 

size=20;
}
if (!StringUtils.isEmpty(data.getSendId()) && !StringUtils.isEmpty(data.getReceiveId())){ 

String msgKeyOne= data.getSendId()+"_"+data.getReceiveId();
String msgKeyTwo = data.getReceiveId()+"_"+data.getSendId();
Criteria orCri1 = new Criteria();
Criteria orCri2 = new Criteria();
orCri1.andOperator(Criteria.where("msgKey").is(msgKeyOne));
orCri2.andOperator(Criteria.where("msgKey").is(msgKeyTwo));
Query query = new Query().addCriteria(new Criteria().orOperator(orCri1,orCri2));
long total = mongoTemplate.count(query,ChatMsg.class);
Pageable pageable = PageRequest.of(page,size, Sort.by(Sort.Direction.DESC,"createTime"));
query.with(pageable);
List<ChatMsg> list = mongoTemplate.find(query,ChatMsg.class);
HashMap<String, Object> map = new HashMap<>();
if (list != null) { 

for (ChatMsg chatMsg:list){ 

chatMsg.setName(chatMsg.getChatMsg());
if (chatMsg.getSendId().equals(data.getSendId())){ 

chatMsg.setFlag(1);
}else { 

chatMsg.setFlag(0);
}
}
Map<String, Object> pageMap = new HashMap<>();
map.put("list", list);
pageMap.put("total", total);
pageMap.put("pageSize", data.getPageSize());
pageMap.put("currentPage", data.getCurrentPage());
map.put("pager", pageMap);
}
Query queryMsg = new Query().addCriteria(Criteria.where("readState").is("0").and("sendId").is(data.getReceiveId()).and("receiveId").is(data.getSendId()));
Update update = new Update().set("readState","1");
mongoTemplate.updateFirst(queryMsg,update,ChatMsg.class);
mav.put("state",true);
mav.put("data",map);
return mav;
}else { 

mav.put("state",false);
mav.put("msg","非法参数");
return mav;
}
}
/** * @description: 未读消息条数 * @author: lvyq * @date: 2021/11/16 14:20 * @version 1.0 */
@Override
public JSONObject getUnReadNum(ChatMsg data) { 

JSONObject mav = new JSONObject();
Query query = new Query();
query.addCriteria(Criteria.where("readState").is("0").and("sendId").is(data.getSendId()).and("receiveId").is(data.getReceiveId()));
long count = mongoTemplate.count(query,ChatMsg.class);
mav.put("state",true);
mav.put("unReadNum",count);
return mav;
}
/** * @description: 获取websocket地址 * @author: lvyq * @date: 2021/11/16 14:47 * @version 1.0 */
@Override
public JSONObject getWebUrl() { 

JSONObject mav = new JSONObject();
mav.put("state",true);
mav.put("webUrl",WebUrl);
return mav;
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/179383.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号