vhr项目地址:https://github.com/lenve/vhr
在线聊天功能是为了方便HR快速交流,由于HR人数有限,因此这里并未考虑高并发问题,小伙伴思考问题一定要结合上下文环境。OK,我们先来看看效果图:
在线聊天效果图登陆成功后,点击右上角的闹铃图标,进入到消息页面,点击 好友聊天 选项卡,效果如下:
此时换个浏览器,或者使用chrome中的多用户模式再打开一个浏览器,以另外一个用户身份登录,开始进行聊天,聊天页面如下:
如果系统管理员正在和韩愈聊天,此时李白发来的消息,则李白的姓名旁会有提示:
系统消息效果图
只有管理员具备发送系统消息的权限,管理员的系统消息页面如下:
普通HR的系统消息页面没有发送按钮,发送系统消息页面如下:
消息发送成功之后,会有红点提示未读消息,如下:
OK,大致效果就是这样,功能还不是很完善,后期有时间再进行修补。
下面我们来聊聊实现思路:
在线聊天使用了SpringBoot+WebSocket实现,为了保证用户隐私,所有的聊天数据都保存在系统本地,服务器只进行了数据转发。OK,那接下来,我们来看下大致的实现步骤。
服务端服务端首先加入websocket依赖,如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
创建WebSocket的配置类,如下:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/ws/endpointChat").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue","/topic");
}
}
这里我并未使用原生的websocket协议,而是使用了websocket的子协议stomp,方便一些。消息代理既使用了/queue
,又使用了/topic
,主要是因为我这里既有点对点的单聊(queue),也有发送系统消息的群聊(topic)。
创建websocket处理类Controller,如下:
@Controller
public class WsController {
@Autowired
SimpMessagingTemplate messagingTemplate;
@MessageMapping("/ws/chat")
public void handleChat(Principal principal, String msg) {
String destUser = msg.substring(msg.lastIndexOf(";") + 1, msg.length());
String message = msg.substring(0, msg.lastIndexOf(";"));
messagingTemplate.convertAndSendToUser(destUser, "/queue/chat", new ChatResp(message, principal.getName()));
}
}
消息协议很简单:发送来的消息,最后一个;
后面跟的是该条消息要发送到哪个用户,这里通过字符串截取将之提取出来。响应消息包含两个字段,一个是消息内容,一个是该条消息由谁发送。
OK,如此之后,我们的服务端就写好了,很简单。
前端前端代码稍微复杂,我这里主要和小伙伴介绍下我的大致思路和核心代码,具体代码小伙伴可以star源码进行研究。
首先,当用户登录成功之后,我就发起websocket的连接,将ws连接起来,ws的代码我主要写在了store中,如下:
connect(context){
context.state.stomp = Stomp.over(new SockJS("/ws/endpointChat"));
context.state.stomp.connect({}, frame=> {
context.state.stomp.subscribe("/user/queue/chat", message=> {
var msg = JSON.parse(message.body);
var oldMsg = window.localStorage.getItem(context.state.user.username + "#" + msg.from);
if (oldMsg == null) {
oldMsg = [];
oldMsg.push(msg);
window.localStorage.setItem(context.state.user.username + "#" + msg.from, JSON.stringify(oldMsg))
} else {
var oldMsgJson = JSON.parse(oldMsg);
oldMsgJson.push(msg);
window.localStorage.setItem(context.state.user.username + "#" + msg.from, JSON.stringify(oldMsgJson))
}
if (msg.from != context.state.currentFriend.username) {
context.commit("addValue2DotMap", "isDot#" + context.state.user.username + "#" + msg.from);
}
//更新msgList
var oldMsg2 = window.localStorage.getItem(context.state.user.username + "#" + context.state.currentFriend.username);
if (oldMsg2 == null) {
context.commit('updateMsgList', []);
} else {
context.commit('updateMsgList', JSON.parse(oldMsg2));
}
});
}, failedMsg=> {
});
}
连接成功之后,就可以准备接收服务端的消息了,接收到服务端的消息后,数据保存在localStorage中,保存格式是 当前用户名#消息发送方用户名:[{from:'消息发送方',msg:'消息内容'}]
,注意后面的是一个json数组,整个存储的key之所以用当前用户名#消息发送方用户名
是为了避免同一个浏览器多个用户登录所产生的数据紊乱,OK,这样两个人的聊天记录都将保存在这个数组中。在聊天展示页面,当数组中的数据发生变化时,自动更新。
在聊天页面,通过stomp发送消息,如下:
this.$store.state.stomp.send("/ws/chat", {}, this.msg + ";" + this.currentFriend.username);
注意每条消息的内容除了内容本身外,还要加上当前发送者的名字。
每次发送成功后更新聊天页面即可。更新聊天页面代码如下:
<template v-for="msg in msgList">
<!--发送来的消息-->
<div
style="display: flex;justify-content: flex-start;align-items: center;box-sizing: border-box;"
v-if="msg.from==currentFriend.username">
<img :class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="currentFriend.userface" class="userfaceImg">
<div
style="display: inline-flex;border-style: solid;border-width: 1px;border-color: #20a0ff;border-radius: 5px;padding: 5px 8px 5px 8px">
{{msg.msg}}
</div>
</div>
<!--发出去的消息-->
<div v-if="msg.from!=currentFriend.username"
style="display: flex;justify-content: flex-end;align-items: center;box-sizing: border-box;">
<div
style="display: inline-flex;border-style: solid;border-width: 1px;border-color: #20a0ff;border-radius: 5px;padding: 5px 8px 5px 8px;margin-right: 3px;background-color: #9eea6a">
{{msg.msg}}
</div>
<img :class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="currentUser.userface" class="userfaceImg">
</div>
</template>
如果消息中的from字段的值,就是当前聊天的用户名,则是发送来的消息,否则就是发出去的消息,不同的消息设置不同的样式即可。
核心代码基本如此,但是琐碎的细节其实是比较多的,小伙伴可以star源码进行研究。
vhr项目地址:https://github.com/lenve/vhr
共同学习,写下你的评论
评论加载中...
作者其他优质文章