本项目为基于Springboo+vue+websocket的前后端分离的实时多人在线聊天管理系统
本项目实现功能:
- 登录注册
- 添加搜索好友
- 好友上线提醒
- 好友上线下线标识
- 好友发送消息通知(红点数字)
- 好友申请通知
- 我的申请记录
- 单人聊天
- 同时多人聊天
- 不在线信息保存及上线信息提示等
注意:本项目为简易版项目,可能会存在部分bug,且功能并没有完善好,很适合学习使用,禁止商用!!后续会把项目继续完善好!
后面会把添加更多功能,包括:
- 好友列表搜索
- 好友分类
- 个人信息编辑
- 设置好友备注
- 群聊功能
暂时就想到这些 这些也是下一步的开发计划 !
本项目源代码分为前后端以及上传到资源中了
目前项目是收费的,但是价格很低,前后端一共不到20块钱,算大家犒劳一下作者了!已开启不允许自动调整价格!(不喜勿喷)
1.首页
2.登录页面
3.聊天页面
4.搜索添加联系人
5.我的消息通知
6.注册页面
7.好友在线与不在线标识
8.我的申请列表
9.多人同时聊天
10.好友上线通知
11.我的消息内容
12.未读消息提示
前端主要websocket链接代码
initConnect() {
this.ws = new WebSocket(`ws://localhost:8080/talk/imserver/` + this.user.no);//websocket连接地址
this.ws.onopen = () => {
console.log('WebSocket连接已建立');
this.isConnected = true;
// 建立心跳定时器
this.heartbeatInterval = setInterval(() => {
if (this.ws.readyState === 1) {
this.ws.send(JSON.stringify({ type: 'heartbeat' }));
console.log('心跳开启');
}
}, this.heartbeatTimeout);
};
this.ws.onmessage = (event) => {
const dataMessage = JSON.parse(event.data);
if (dataMessage.type && dataMessage.type == 'heartbeat') {
//心跳
return;
} else if (dataMessage.type && dataMessage.type == 'connect') {
//连接成功的信息
return;
} else if (dataMessage.type && dataMessage.type == 'up') {
//好友上线的信息
this.pList.map(item=>{
if(item.no == dataMessage.no){
item.isRun = 1;
this.$notify({
title: '上线通知',
message: '您的好友"'+item.name+'"已上线',
position: 'bottom-right',
type:'success'
});
}
});
return;
} else if (dataMessage.type && dataMessage.type == 'down') {
//好友下线的信息
this.pList.map(item=>{
if(item.no == dataMessage.no){
item.isRun = 0;
}
});
return;
}
//如果当前聊天的人不是发信息的人,则需要给下标且好友中的noReadNum需要加1
//如果是当前聊天的人 则发请求将聊天记录未读信息设置为已读
//当前聊天的id由后台给前端dataMessage.id
console.log("this.activePeo",this.activePeo);
if(dataMessage.fromno != this.activePeo.no){
this.pList.map(item=>{
if(item.no == dataMessage.fromno){
item.noReadNum += 1;
}
})
}else{
//接收的是内容 from text
this.activePeo.talkList.push(dataMessage);
updateRecord({id:dataMessage.id,isread:1}).then(res=>{
console.log(res);
})
}
this.$forceUpdate();
this.$nextTick(() => {
let scrollEl = this.$refs.mianscroll;
scrollEl.scrollTo({ top: scrollEl.scrollHeight, behavior: 'smooth' });
});
console.log(dataMessage, '接收到的信息');
};
this.ws.onerror = () => {
console.error('WebSocket连接发生错误');
};
this.ws.onclose = () => {
console.log('WebSocket连接已关闭');
this.ws.send('close');
this.isConnected = false;
// 清除心跳定时器
clearInterval(this.heartbeatInterval);
};
},
后端websocket代码:
package com.ww.talk.component;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ww.talk.dao.FriendsDao;
import com.ww.talk.entity.TalkRecord;
import com.ww.talk.entity.User;
import com.ww.talk.service.FriendsService;
import com.ww.talk.service.TalkRecordService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author websocket服务
*/
@ServerEndpoint(value = "/imserver/{username}")
@Component
public class WebSocketServer {
private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
/**
* 记录当前在线连接数
*/
public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();
@Resource
private FriendsService friendsService;
@Resource
private TalkRecordService talkRecordService;
private static WebSocketServer webSocketServer ;
@PostConstruct //通过@PostConstruct实现初始化bean之前进行的操作
public void init() {
webSocketServer = this;
webSocketServer.friendsService = this.friendsService;
webSocketServer.talkRecordService = this.talkRecordService;
// 初使化时将已静态化的testService实例化
}
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("username") String username) {
sessionMap.put(username, session);
log.info("有新用户加入,username={}, 当前在线人数为:{}", username, sessionMap.size());
JSONObject result = new JSONObject();
JSONArray array = new JSONArray();
result.put("users", array);
result.put("type", "connect");
for (Object key : sessionMap.keySet()) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("username", key);
// {"username", "zhang", "username": "admin"}
array.add(jsonObject);
}
//好友上线发送上线消息
sendMyFriend(username,"up");
// {"users": [{"username": "zhang"},{ "username": "admin"}]}
// sendAllMessage(JSONUtil.toJsonStr(result)); // 后台发送消息给所有的客户端
sendMessage(JSONUtil.toJsonStr(result),session);
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(Session session, @PathParam("username") String username) {
//好友下线发送下线消息
sendMyFriend(username,"down");
sessionMap.remove(username);
log.info("有一连接关闭,移除username={}的用户session, 当前在线人数为:{}", username, sessionMap.size());
}
/**
* 收到客户端消息后调用的方法
* 后台收到客户端发送过来的消息
* onMessage 是一个消息的中转站
* 接受 浏览器端 socket.send 发送过来的 json数据
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session, @PathParam("username") String username) {
log.info("服务端收到用户username={}的消息:{}", username, message);
if(message.equals("close")){
onClose(session,username);
return;
}
JSONObject obj = JSONUtil.parseObj(message);
String toUsername = obj.getStr("to"); // to表示发送给哪个用户,比如 admin
String text = obj.getStr("text"); // 发送的消息文本 hello
String type = obj.getStr("type"); // 发送的消息类型 heartbeat 心跳
if(type!=null && type.equals("heartbeat")){
//心跳直接返回
this.sendMessage(obj.toString(),session);
}else {
// {"to": "admin", "text": "聊天文本"}
Session toSession = sessionMap.get(toUsername); // 根据 to用户名来获取 session,再通过session发送消息文本
TalkRecord talkRecord = new TalkRecord();
talkRecord.setFromno(Integer.parseInt(username));
talkRecord.setTono(Integer.parseInt(toUsername));
talkRecord.setSendtext(text);
talkRecord.setSendtime(DateUtil.now());
talkRecord.setIsread(0);//默认未读 在点击的时候才会将当前对象设置为已读
webSocketServer.talkRecordService.insert(talkRecord);
if (toSession != null) {
// 服务器端 再把消息组装一下,组装后的消息包含发送人和发送的文本内容
// {"from": "zhang", "text": "hello"}
JSONObject jsonObject = new JSONObject();
jsonObject.put("from", username); // from 是 zhang
jsonObject.put("to", toUsername); // from 是 zhang
jsonObject.put("text", text); // text 同上面的text
jsonObject.put("talkId", talkRecord.getId()); // text 同上面的text
this.sendMessage(JSONUtil.parse(talkRecord).toString(), toSession);
log.info("发送给用户username={},消息:{}", toUsername, jsonObject.toString());
} else {
//不在线存入数据库中
log.info("发送失败,未找到用户username={}的session", toUsername);
}
}
}
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误");
error.printStackTrace();
}
/**
* 服务端发送消息给客户端
*/
private void sendMessage(String message, Session toSession) {
try {
log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);
toSession.getBasicRemote().sendText(message);
} catch (Exception e) {
log.error("服务端发送消息给客户端失败", e);
}
}
/**
* 服务端发送消息给所有客户端
*/
private void sendAllMessage(String message) {
try {
for (Session session : sessionMap.values()) {
log.info("服务端给客户端[{}]发送消息{}", session.getId(), message);
session.getBasicRemote().sendText(message);
}
} catch (Exception e) {
log.error("服务端发送消息给客户端失败", e);
}
}
/**
* 发送给在线好友消息
* @param username 当前人的no
* @param type 类型 up 上线 down 下线
*/
public void sendMyFriend(String username,String type){
Integer cNo = Integer.parseInt(username);
List<Map> all = webSocketServer.friendsService.getMyFriend(cNo);
for(Map map : all){
//拿出好友的信息
Integer no = Integer.parseInt(map.get("userno").toString()) == cNo?Integer.parseInt(map.get("fno").toString()):Integer.parseInt(map.get("userno").toString());
//发送给在线的好友
if(sessionMap.get(no.toString())!=null){
//通过websocket查询一下当前好友在不在线
JSONObject jsonObject = new JSONObject();
jsonObject.put("no", cNo); // 当前的人
jsonObject.put("type", type); // 类型
this.sendMessage(jsonObject.toString(), sessionMap.get(no.toString()));
}
}
}
}
数据库代码在后端代码中!!
后端代码
前端代码
如果项目对大家有帮助?麻烦大家帮忙点点赞,点点收藏啦!谢谢大家!!