校园互助交易平台聊天交流模块详细设计与实现
一、需求分析
校园互助交易平台是为满足学生互相之间的需求而开发的,其聊天交流模块是平台中非常重要的一部分。该模块需要实现以下功能:
- 学生之间可以进行即时聊天。
- 学生可以在聊天中发送文字、图片、语音等多种信息形式。
- 学生可以创建、加入群聊。
- 学生可以在群聊中发送文字、图片、语音等多种信息形式。
- 学生可以查看聊天记录。
- 学生可以在聊天中发送表情。
二、设计思路
-
聊天功能的实现需要使用 WebSocket 协议,因此需要在后端使用 WebSocket 技术。
-
聊天记录需要存储在数据库中,因此需要在后端使用数据库技术。
-
前端需要使用 Vue.js 技术实现聊天界面。
-
在后端需要设计聊天室的数据结构,包括聊天室的 ID、聊天室的成员、聊天记录等信息。
-
后端需要实现聊天室的创建、加入和退出功能。
-
后端需要实现聊天记录的存储和查询功能。
-
前端需要实现聊天界面的设计和消息发送功能。
三、代码实现
- 后端实现
后端使用 Node.js 作为开发语言,使用 WebSocket 技术实现聊天功能,使用 MongoDB 存储聊天记录。
(1)聊天室数据结构设计
聊天室数据结构需要包括聊天室的 ID、聊天室的成员、聊天记录等信息。
{
roomId: String, // 聊天室 ID
members: [{
userId: String, // 用户 ID
socketId: String // WebSocket 连接 ID
}],
messages: [{
from: String, // 发送者 ID
to: String, // 接收者 ID(可以是聊天室 ID)
type: String, // 消息类型(text、image、voice 等)
content: String, // 消息内容
time: Date // 发送时间
}]
}
(2)创建、加入和退出聊天室功能实现
创建聊天室时,需要生成一个聊天室 ID,并将创建者加入聊天室成员列表中。
function createRoom(userId, socketId) {
const roomId = generateRoomId(); // 生成聊天室 ID
const members = [{ userId, socketId }]; // 将创建者加入聊天室成员列表中
const messages = []; // 初始化聊天记录
const room = { roomId, members, messages };
rooms.push(room); // 将聊天室添加到聊天室列表中
return room;
}
加入聊天室时,需要将用户加入聊天室成员列表中。
function joinRoom(roomId, userId, socketId) {
const room = getRoomById(roomId);
if (room) {
room.members.push({ userId, socketId }); // 将用户加入聊天室成员列表中
return room;
}
return null;
}
退出聊天室时,需要将用户从聊天室成员列表中删除。
function leaveRoom(roomId, userId) {
const room = getRoomById(roomId);
if (room) {
const index = room.members.findIndex(member => member.userId === userId);
if (index !== -1) {
room.members.splice(index, 1); // 将用户从聊天室成员列表中删除
}
return room;
}
return null;
}
(3)聊天记录存储和查询功能实现
聊天记录存储使用 MongoDB 数据库。使用 Mongoose 模块来操作 MongoDB 数据库。
const mongoose = require('mongoose');
const messageSchema = new mongoose.Schema({
from: String,
to: String,
type: String,
content: String,
time: Date
});
const roomSchema = new mongoose.Schema({
roomId: String,
members: [{
userId: String,
socketId: String
}],
messages: [messageSchema]
});
const Room = mongoose.model('Room', roomSchema);
async function saveMessage(roomId, message) {
await Room.updateOne(
{ roomId },
{ $push: { messages: message } }
);
}
async function getMessages(roomId) {
const room = await Room.findOne({ roomId });
if (room) {
return room.messages;
}
return null;
}
(4)WebSocket 实现聊天功能
使用 ws 模块来实现 WebSocket 通信。
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws, req) => {
const userId = getUserIdFromReq(req); // 获取用户 ID
const roomId = getRoomIdFromReq(req); // 获取聊天室 ID
// 加入聊天室
const room = joinRoom(roomId, userId, ws.id);
if (!room) {
ws.close();
return;
}
// 发送聊天记录
const messages = getMessages(roomId);
if (messages) {
ws.send(JSON.stringify(messages));
}
// 监听消息
ws.on('message', async (data) => {
try {
const message = JSON.parse(data);
message.from = userId;
message.time = new Date();
message.to = roomId;
room.messages.push(message);
saveMessage(roomId, message);
broadcastMessage(room, message);
} catch (error) {
console.log(error);
}
});
// 监听关闭
ws.on('close', () => {
leaveRoom(roomId, userId);
broadcastLeave(room, userId);
});
});
function broadcastMessage(room, message) {
room.members.forEach(member => {
const ws = wss.clients.get(member.socketId);
if (ws) {
ws.send(JSON.stringify([message]));
}
});
}
function broadcastLeave(room, userId) {
const message = {
from: 'system',
to: room.roomId,
type: 'text',
content: `${userId} 离开了聊天室`,
time: new Date()
};
broadcastMessage(room, message);
}
(5)完整代码
完整的后端代码如下:
const WebSocket = require('ws');
const express = require('express');
const mongoose = require('mongoose');
const app = express();
// 连接 MongoDB 数据库
mongoose.connect('mongodb://localhost/chatroom', {
useNewUrlParser: true,
useUnifiedTopology: true
});
const messageSchema = new mongoose.Schema({
from: String,
to: String,
type: String,
content: String,
time: Date
});
const roomSchema = new mongoose.Schema({
roomId: String,
members: [{
userId: String,
socketId: String
}],
messages: [messageSchema]
});
const Room = mongoose.model('Room', roomSchema);
const wss = new WebSocket.Server({ server: app.listen(8080) });
const rooms = [];
function generateRoomId() {
return Math.random().toString(36).substr(2, 8);
}
function getUserIdFromReq(req) {
// 从请求中获取用户 ID
}
function getRoomIdFromReq(req) {
// 从请求中获取聊天室 ID
}
function getRoomById(roomId) {
return rooms.find(room => room.roomId === roomId);
}
function createRoom(userId, socketId) {
const roomId = generateRoomId();
const members = [{ userId, socketId }];
const messages = [];
const room = { roomId, members, messages };
rooms.push(room);
return room;
}
function joinRoom(roomId, userId, socketId) {
const room = getRoomById(roomId);
if (room) {
room.members.push({ userId, socketId });
return room;
}
return null;
}
function leaveRoom(roomId, userId) {
const room = getRoomById(roomId);
if (room) {
const index = room.members.findIndex(member => member.userId === userId);
if (index !== -1) {
room.members.splice(index, 1);
}
return room;
}
return null;
}
async function saveMessage(roomId, message) {
await Room.updateOne(
{ roomId },
{ $push: { messages: message } }
);
}
async function getMessages(roomId) {
const room = await Room.findOne({ roomId });
if (room) {
return room.messages;
}
return null;
}
wss.on('connection', (ws, req) => {
const userId = getUserIdFromReq(req);
const roomId = getRoomIdFromReq(req);
const room = joinRoom(roomId, userId, ws.id);
if (!room) {
ws.close();
return;
}
const messages = getMessages(roomId);
if (messages) {
ws.send(JSON.stringify(messages));
}
ws.on('message', async (data) => {
try {
const message = JSON.parse(data);
message.from = userId;
message.time = new Date();
message.to = roomId;
room.messages.push(message);
saveMessage(roomId, message);
broadcastMessage(room, message);
} catch (error) {
console.log(error);
}
});
ws.on('close', () => {
leaveRoom(roomId, userId);
broadcastLeave(room, userId);
});
});
function broadcastMessage(room, message) {
room.members.forEach(member => {
const ws = wss.clients.get(member.socketId);
if (ws) {
ws.send(JSON.stringify([message]));
}
});
}
function broadcastLeave(room, userId) {
const message = {
from: 'system',
to: room.roomId,
type: 'text',
content: `${userId} 离开了聊天室`,
time: new Date()
};
broadcastMessage(room, message);
}
- 前端实现
前端使用 Vue.js 技术实现聊天界面。
(1)聊天界面设计
聊天界面分为左侧聊天列表和右侧聊天内容区域两部分。
左侧聊天列表显示所有聊天室和群聊,点击可进入对应的聊天室或群聊。
右侧聊天内容区域显示聊天记录和聊天输入框。聊天记录按照时间顺序排列,支持文本、图片、语音等多种消息类型。
(2)聊天模块实现
聊天模块使用 Vue.js 技术实现,主要包括消息发送、消息接收、聊天室列表和聊天记录等功能。
<template>
<div class="chat">
<div class="chat-list">
<div class="chat-room" v-for="room in rooms" :key="room.roomId" @click="enterRoom(room)">
<div class="room-name">{{ room.roomId }}</div>
<div class="room-members">{{ room.members.length }} 人</div>
</div>
</div>
<div class="chat-content">
<div class="message" v-for="message in messages" :key="message.time">
<div class="message-time">{{ formatTime(message.time) }}</div>
<div class="message-body" :class="{ 'message-from-me': message.from === userId }">
<div v-if="message.type === 'text'" class="message-text">{{ message.content }}</div>
<div v-if="message.type === 'image'" class="message-image">
<img :src="message.content">
</div>
<div v-if="message.type === 'voice'" class="message-voice">{{ message.content }}</div>
</div>
</div>
<div class="chat-input">
<input type="text" v-model="text" @keydown.enter="sendMessage">
<button @click="sendImage">发送图片</button>
<button @click="sendVoice">发送语音</button>
<button @click="sendEmoji">发送表情</button>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
userId: '', // 用户 ID
roomId: '', // 聊天室 ID
rooms: [], // 聊天室列表
messages: [], // 聊天记录
text: '', // 文本消息内容
image: null, // 图片消息内容
voice: null, // 语音消息内容
emoji: null // 表情消息内容
};
},
created() {
this.initWebSocket();
this.loadRooms();
},
methods: {
initWebSocket() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
this.ws = new WebSocket(`${protocol}//localhost:8080`);
this.ws.onopen = () => {
console.log('WebSocket open');
};
this.ws.onmessage = (event) => {
const messages = JSON.parse(event.data);
this.messages.push(...messages);
};
this.ws.onclose = () => {
console.log('WebSocket close');
};
},
loadRooms() {
// 加载聊天室列表
},
enterRoom(room) {
// 进入聊天室
},
sendMessage() {
// 发送文本消息
},
sendImage() {
// 发送图片消息
},
sendVoice() {
// 发送语音消息
},
sendEmoji() {
// 发送表情消息
},
formatTime(time) {
// 格式化时间
}
}
};
</script>
(3)完整代码
完整的前端代码如下:
<template>
<div class="chat">
<div class="chat-list">
<div class="chat-room" v-for="room in rooms" :key="room.roomId" @click="enterRoom(room)">
<div class="room-name">{{ room.roomId }}</div>
<div class="room-members">{{ room.members.length }} 人</div>
</div>
</div>
<div class="chat-content">
<div class="message" v-for="message in messages" :key="message.time">
<div class="message-time">{{ formatTime(message.time) }}</div>
<div class="message-body" :class="{ 'message-from-me': message.from === userId }">
<div v-if="message.type === 'text'" class="message-text">{{ message.content }}</div>
<div v-if="message.type === 'image'" class="message-image">
<img :src="message.content">
</div>
<div v-if="message.type === 'voice'" class="message-voice">{{ message.content }}</div>
</div>
</div>
<div class="chat-input">
<input type="text" v-model="text" @keydown.enter="sendMessage">
<button @click="sendImage">发送图片</button>
<button @click="sendVoice">发送语音</button>
<button @click="sendEmoji">发送表情</button>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
userId: '', // 用户 ID
roomId: '', // 聊天室 ID
rooms: [], // 聊天室列表
messages: [], // 聊天记录
text: '', // 文本消息内容
image: null, // 图片消息内容
voice: null, // 语音消息内容
emoji: null // 表情消息内容
};
},
created() {
this.initWebSocket();
this.loadRooms();
},
methods: {
initWebSocket() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
this.ws = new WebSocket(`${protocol}//localhost:8080`);
this.ws.onopen = () => {
console.log('WebSocket open');
};
this.ws.onmessage = (event) => {
const messages = JSON.parse(event.data);
this.messages.push(...messages);
};
this.ws.onclose = () => {
console.log('WebSocket close');
};
},
loadRooms() {
// 加载聊天室列表
},
enterRoom(room) {
// 进入聊天室
},
sendMessage() {
// 发送文本消息
},
sendImage() {
// 发送图片消息
},
sendVoice() {
// 发送语音消息
},
sendEmoji() {
// 发送表情消息
},
formatTime(time) {
// 格式化时间
}
}
};
</script>
<style>
.chat {
display: flex;
height: 100%;
}
.chat-list {
width: 20%;
border-right: 1px solid #ccc;
padding: 10px;
overflow-y: auto;
}
.chat-room {
display: flex;
align-items: center;
padding: 10px;
cursor: pointer;
}
.room-name {
font-size: 16px;
font-weight: bold;
}
.room-members {
margin-left: 10px;
font-size: 1
原文地址: https://www.cveoy.top/t/topic/cRNu 著作权归作者所有。请勿转载和采集!