基于Web的简易聊天室

霜月红枫 2022-01-18 17:05:40

基于Web的简易聊天室

介绍

使用了Vue + Express 架构实现了在线简易聊天室,将前后端分离,使用MongoDB作为数据库,实现了用户注册、登陆、聊天等功能。

本文主要介绍后端部分,通过node.js和Express实现后端功能,采用MongoDB作为数据库,分别为前端提供Http服务和WebSocket服务,包括提供用户注册、登录功能,添加好友、处理好友申请,好友聊天功能。

后端

数据库

数据库采用MongoDB,包含三个表:

  • 用户表(users)

    字段类型说明约束
    _idString表结构id唯一
    nameString用户名唯一
    pwdString密码
    sexNumber性别(0-未知,1-女,2-男)
    birthDate生日
    explainString介绍
    imgUrlString头像链接
    timeDate注册时间
  • 好友表(friends)

    字段类型说明约束
    _idString表结构id唯一
    userIdString用户id外键[users:_id]
    friendIdString好友id外键[users:_id]
    stateNumber好友申请状态(0-已好友,1-申请中,2-已拒绝)
    timeDate好友申请时间
  • 消息表(messages)

    字段类型说明约束
    _idString表结构id唯一
    userIdString用户id外键[users:_id]
    friendIdString好友id外键[users:_id]
    messageString消息内容
    typeNumber消息类型(0-文字,1-图片,2-链接)
    stateString消息状态(0-未读,1-已读)
    timeDate消息发送时间

使用mongoose操作数据库。

img

const mongoose = require('mongoose')
const db = require('../config/db')

const UserSchema = new mongoose.Schema({
    name: {type: String},
    pwd: {type: String},
    sex: {type: Number, default: '0'},
    birth: {type: Date, default: ''},
    explain: {type: String, default: ''},
    imgUl: {type: String, default: 'user.png'},
    time: {type: Date}
})

const FriendSchema = new mongoose.Schema({
    userId: {type: String, ref: 'User'},
    friendId: {type: String, ref: 'User'},
    state: {type: String},          //  0-已为好友,1-申请中,2-对方未同意
    time: {type: Date}
})

const MessageSchema = new mongoose.Schema({
    userId: {type: String, ref: 'User'},
    friendId: {type: String, ref: 'User'},
    message: {type: String},
    type: {type: String},       // 0-文字,1-图片,3-链接
    state: {type: String},      // 0-已读,1-未读
    time: {type: Date}
})

module.exports = db.model('User', UserSchema)
module.exports = db.model('Friend', FriendSchema)
module.exports = db.model('Message', MessageSchema)

Http服务

后端提供http服务,支持注册、登录、获取用户信息、获取好友列表等功能。

  • 注册: POST http://127.0.0.1:3000/register
    请求参数:
    参数类型说明必选
    usenameString用户名true
    passwordString密码true
    返回字段:
    返回字段类型说明必选
    statusBoolean注册是否成功true
    existsBoolean帐号是否已存在true
  • 登录: POST http://127.0.0.1:3000/login

    请求参数:

    参数类型说明必选
    usenameString用户名true
    passwordString密码true

    返回字段:

    返回字段类型说明必选
    statusBoolean登录是否成功true
    tokenString用户tokenfalse
    userInfoJson用户信息false
  • 获取用户信息: POST http://127.0.0.1:3000/getUserInfo

    请求参数:

    参数类型说明必选
    userIdString用户idtrue

    返回字段:

    返回字段类型说明必选
    statusBoolean获取是否成功true
    userInfoJson用户信息false
  • 获取好友列表: POST http://127.0.0.1:3000/getFriends

    请求参数:

    参数类型说明必选
    tokenString用户tokentrue

    返回字段:

    返回字段类型说明必选
    statusBoolean获取是否成功true
    friendsJsonArray好友信息列表false
  • 获取单聊信息列表: POST http://127.0.0.1:3000/pullMessages

    请求参数:

    参数类型说明必选
    tokenString用户tokentrue

    返回字段:

    返回字段类型说明必选
    statusBoolean获取是否成功true
    messagesJsonArray单聊信息列表false
  • 申请添加好友

    POST http://127.0.0.1:3000/addFriend

    请求参数:

    参数类型说明必选
    tokenString用户tokentrue
    userIdString用户idtrue

    返回字段:

    返回字段类型说明必选
    statusBoolean获取是否成功true
    existsBoolean好友关系是否已存在true

代码:

const express = require('express')

const app = express()
const port = 3000

app.use(express.json()) // for parsing application/json
app.use(express.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded

// 前后端分离跨域访问
app.use((req, res, next) => {
    res.header('Access-Control-Allow-Origin', 'http://localhost:8080')
    res.header('Access-Control-Allow-Headers', 'Authorization,X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method' )
    res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PATCH, PUT, DELETE')
    res.header('Allow', 'GET, POST, PATCH, OPTIONS, PUT, DELETE')
    next();
})

app.use(express.static('./assets'))

// 路由
require('./router/index')(app)

app.use((req, res, next) => {
    let err = new Error("Not Found")
    err.Status = 404
    next(err)
})

app.listen(port, () => {
    console.log(`Server listening at http://localhost:${port}`)
})
# router/index
const dbServer = require('../dao/dbServer')
const register = require('../api/register')
const login = require('../api/login')
const getFriends = require('../api/getFriends')
const addFriend = require('../api/addFriend')
const pushMessage = require('../api/pushMessage')
const getUserInfo = require('../api/getUserInfo')
const pullMessages = require('../api/pullMessages')
const dealFriend = require('../api/dealFriend')
const searchFriend = require('../api/searchFriend')

module.exports = function(app) {
    app.get('/test', (req, res) => {
        res.send('Hello Test!')
    })
    app.post('/register', (req, res) => {
        register.register(req, res)
    })
    app.post('/login', (req, res) => {
        login.login(req, res)
    })
    app.post('/getUserInfo', (req, res) => {
        getUserInfo.getUserInfo(req, res)
    })
    app.post('/getFriends', (req, res) => {
        getFriends.getFriends(req, res)
    })
    app.post('/pullMessages', (req, res) => {
        pullMessages.pullMessages(req, res)
    })
    app.post('/searchFriend', (req, res) => {
        searchFriend.searchFriend(req, res)
    })
    app.post('/addFriend', (req, res) => {
        addFriend.addFriend(req, res)
    })
}

WebSocket服务

后端还提供WebSocket服务,支持添加好友、好友聊天等功能,将添加好友、发送的消息广播给好友。

  • 申请添加好友: ws://127.0.0.1:7000请求数据:
    {
        type: "addFriend",
        data: { 
            token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYxZDEwNmRkZjAxZDNlODRlYmVmNTQ2MCIsImRhdGEiOiIyMDIyLTAxLTE3VDEyOjMxOjIxLjU2MloiLCJpYXQiOjE2NDI0MjI2ODEsImV4cCI6MTY0MjUwOTA4MX0.Zk4jcc_BsVBb_ri-do8lSvPfWxU1Q8mV_CBfljxDLiU",
            userId: "61e561920bd388f7073927b1"
        }
    }
    
    关播数据:
    {
        type: "addFriend",
        data: {
            _id: new ObjectId("61e5700e0bd388f7073927ed"),
            userId: "61d106ddf01d3e84ebef5460",
            friendId: "61e556c573f70498d7087235",
            state: "0",
            time: "2022-01-17T13:33:02.000Z",
            __v: 0
      }
    }
    
  • 处理好友申请: ws://127.0.0.1:7000请求数据:
    {
        type: "dealFriend",
        data: { 
            token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYxZDEwNmRkZjAxZDNlODRlYmVmNTQ2MCIsImRhdGEiOiIyMDIyLTAxLTE3VDEyOjMxOjIxLjU2MloiLCJpYXQiOjE2NDI0MjI2ODEsImV4cCI6MTY0MjUwOTA4MX0.Zk4jcc_BsVBb_ri-do8lSvPfWxU1Q8mV_CBfljxDLiU",
            userId: "61e561920bd388f7073927b1",
            agree: true
        }
    }
    
    广播数据:
    {
        type: "dealFriend",
        data: {
            _id: new ObjectId("61e5700e0bd388f7073927ed"),
            userId: "61d106ddf01d3e84ebef5460",
            friendId: "61e556c573f70498d7087235",
            state: "0",
            __v: 0
      }
    }
    
  • 发送单聊信息: ws://127.0.0.1:7000接收数据:
    {
        type: "sendMsg",
        data: {
            token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjYxZDEwNmRkZjAxZDNlODRlYmVmNTQ2MCIsImRhdGEiOiIyMDIyLTAxLTE3VDEyOjMxOjIxLjU2MloiLCJpYXQiOjE2NDI0MjI2ODEsImV4cCI6MTY0MjUwOTA4MX0.Zk4jcc_BsVBb_ri-do8lSvPfWxU1Q8mV_CBfljxDLiU",
            userId: "61e561920bd388f7073927b1",
            message: "Hello World!",
            type: "0",
        }
    }
    
    广播数据:
    {
        type: "senMsg",
        data: {
            _id: new ObjectId("61e5700e0bd388f7073927ed"),
            userId: "61d106ddf01d3e84ebef5460",
            friendId: "61e556c573f70498d7087235",
            message: "Hello World!",
            type: "0",
            state: "0",
            time: "2022-01-17T13:33:02.000Z",
            __v: 0
      }
    }
    

代码:

const ws = require('ws')

const wss = new ws.WebSocketServer({ port: 7000 })

wss.on('open', () => {
    console.log('WebSocket Open')
})

wss.on('close', () => {
    console.log('WebSocket Close')
})

wss.on('error', () => {
    console.log('WebSocket Error')
})

wss.on('connection', function connection(ws) {
    ws.on('message', function message(data) {
        let sData = JSON.parse(data)
        console.log('received: %s', data)
        if (sData.type === 'sendMsg') {
            pushMessage.pushMessageSocket(sData.data).then((res) => {
                console.log(res)
                wss.clients.forEach(function each(c) {
                    c.send(JSON.stringify({ type:'sendMsg', data: res }), { binary: false })
                })
            })
        } else if (sData.type === 'addFriend') {
            addFriend.addFriendSocket(sData.data).then((res) => {
                // console.log(res)
                wss.clients.forEach(function each(c) {
                    c.send(JSON.stringify({ type:'addFriend', data: res }), { binary: false })
                })
            })
        } else if (sData.type === 'dealFriend') {
            dealFriend.dealFriendSocket(sData.data).then((res) => {
                console.log(res)
                if (res) {
                    if (sData.data.agree === true) {
                        sData.data['state'] = '0'
                    } else if (sData.data.agree === false) {
                        sData.data['state'] = '2'
                    }
                    let payload = jwt.verifyToken(sData.data.token)
                    sData.data['friendId'] = payload.id
                    wss.clients.forEach(function each(c) {
                        c.send(JSON.stringify(sData), { binary: false })
                    })
                }
            })
        }
    })
})

前端

使用UI组件库Elment Plus、路由管理器Vue Router、状态管理模式Vuex、http库Axios等。前端的实现效果如下:

路由分发

进入聊天页面之前会先从服务端获取好友列表和历史消息列表,并建立WebSocket客户端用于聊天。

WebSocket客户端

客户端保存WebSocket对象在状态管理中,通过ws.send()发送消息,通过onMessage函数来接受webSocket服务端广播的消息。

状态管理

状态管理保存用户信息、用户token、好友列表、消息列表等信息,并在actions中实现请求后端的接口。

注册登录页面

注册

img

登陆

img

聊天页面

用户信息

img

申请好友

img

处理好友申请

img

好友聊天

img

img

总结

  • 本项目实现了用户注册和登录,并实现了用户单独聊天的功能。

作者:NP592

👉 前端具体实现见这里

...全文
321 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

571

社区成员

发帖
与我相关
我的任务
社区描述
软件工程教学新范式,强化专项技能训练+基于项目的学习PBL。Git仓库:https://gitee.com/mengning997/se
软件工程 高校
社区管理员
  • 码农孟宁
加入社区
  • 近7日
  • 近30日
  • 至今

试试用AI创作助手写篇文章吧