一、需求分析

校园互助交易平台是为满足学生互相之间的需求而开发的,其聊天交流模块是平台中非常重要的一部分。该模块需要实现以下功能:

  1. 学生之间可以进行即时聊天。
  2. 学生可以在聊天中发送文字、图片、语音等多种信息形式。
  3. 学生可以创建、加入群聊。
  4. 学生可以在群聊中发送文字、图片、语音等多种信息形式。
  5. 学生可以查看聊天记录。
  6. 学生可以在聊天中发送表情。

二、设计思路

  1. 聊天功能的实现需要使用 WebSocket 协议,因此需要在后端使用 WebSocket 技术。

  2. 聊天记录需要存储在数据库中,因此需要在后端使用数据库技术。

  3. 前端需要使用 Vue.js 技术实现聊天界面。

  4. 在后端需要设计聊天室的数据结构,包括聊天室的 ID、聊天室的成员、聊天记录等信息。

  5. 后端需要实现聊天室的创建、加入和退出功能。

  6. 后端需要实现聊天记录的存储和查询功能。

  7. 前端需要实现聊天界面的设计和消息发送功能。

三、代码实现

  1. 后端实现

后端使用 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);
}
  1. 前端实现

前端使用 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 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录