Skip to content

学完了 WebSocket 后,我们做了这个聊天室项目。

用户登录之后可以添加别的用户为好友。

发送好友申请后,对方同意之后就会出现在好友列表,然后就可以聊天了。

可以创建群聊,添加别的用户进入群聊。

群聊里可以多人聊天。

聊天记录做了服务端的存储,并且可以收藏聊天记录。

整个项目是围绕 WebSocket 来的,用的 socket.io 的包。

socket.io 支持把 socket 加入 room,房间内可以广播通信。

这样一对一聊天、群聊只是房间内人数的不同。

我们围绕这个聊天过程,做了一系列数据库的设计

首先,房间名存储的是 id

我们创建了一个 chatroom 表来存储聊天室的信息。

这样你可以根据 id 查出这个聊天室的名字、是一对一聊天还是群聊等:

字段名数据类型描述
idINT聊天室ID
nameVARCHAR(50)群聊名
typeBOOLEAN类型(单聊、群聊)
create_timeDATETIME创建时间
update_timeDATETIME更新时间

这个聊天室表和用户表是多对多关联:

在中间表里存储了这种关联关系:

user_chatroom:

字段名数据类型描述
user_idINT用户ID
chatroom_idINT聊天室ID

那我们就可以查出这个聊天室里有多少人。

反过来也可以查出用户在那些聊天室里。

用户登录之后,和服务端建立 WebSocket 连接的时候就可以根据 userId 查出他所在的聊天室,然后把它加入对应 room。

之后,用户就可以这些房间聊天了。

每条聊天记录都会做保存,存在聊天记录表里:

每条聊天记录关联了发送用户、聊天室的信息。

这样用户进入聊天室的时候,就可以关联查出历史聊天记录,并且显示是哪个用户发的。

收藏表就是记录了哪条用户、收藏了哪条聊天历史。

然后用户和用户的好友关系就是多对多关联:

但是添加好友关系之间,会发好友申请,放在 friend_request 表里,通过后才会加入好友关系表。

你看,通过一个简单的房间的 id,我们就可以关联查出这么多东西:

你从聊天室表为起点往左边看:

可以关联查出来聊天记录、聊天室成员、成员的好友列表、成员的收藏等。

这就是关系型数据库的魅力,通过外键关联,你可以存储很多的关联的信息。

业务的复杂度一般取决于你关联存储的东西的多少。

所以很多人用表的数量来衡量业务复杂度,有一定的道理。

回顾下我们做这个项目的整个过程:

首先我们做了需求分析,分析了下有哪些功能,并画了原型图。

这一步主要是明确做什么。

最后我们做出来的也是符合这些需求分析和原型图的。

然后我们设计了下技术方案,做了技术选型:

数据库设计:

分析了下接口:

接下来进入开发:

我们首先创建了 nest 项目,并引入了 prisma 和 redis,实现了注册功能。

通过 prisma 的 migrate 功能,生成迁移 sql 并同步到数据库。

这样,线上也可以用这些 sql 来初始化数据库。

之前用 TypeORM 也要做数据库迁移,不过需要自己准备这些 sql:

从数据库迁移方面来说,prisma 确实方便很多。

之后加上了登录鉴权,做了 token 的自动续期,也就是访问接口后在 header 返回新 token,这样比双 token 的方案简单。

然后封装了 @RequireLogin 和 @UserInfo 两个自定义装饰器。

实现了用户密码修改、用户信息修改的功能。

之后进入好友模块的开发,实现了好友列表和添加好友的功能。

好友关系就是用户和用户的多对多关系,需要一个中间表来保存。

好友列表就是根据当前用户 id 查询它的好友关系。

添加好友需要创建一个好友请求,状态为申请中,同意之后改为已同意,然后添加一条好友关系的记录。

删除好友的话就是删除好友关系表中对应的记录。

之后进入页面开发,写了登录、注册、修改密码、修改信息的页面。

因为我们之前写过,所以这里大部分是复制过来的。

之后又写了好友列表、好友申请、发送好友请求的页面:

上面都是准备工作,接下来进入聊天功能的开发:

我们基于 socket.io 实现了 websocket 服务的前后端。

发送 joinRoom 消息的时候把 client socket 加入房间,房间名为 chatroomId

发送 sendMessage 消息的时候把 message 发送给房间的所有用户。

我们还做了聊天记录的保存,每个房间聊天的时候都会把聊天内容存到数据库里。

之后写了聊天的前端部分,通过 socket.io-client 来实现。

监听服务端的 message 消息,有新消息的时候添加到聊天记录里,并通过 scrollIntoView 滚动到底部。

然后串联起了从好友列表,点击聊天进入对应聊天室:

还有从群聊列表点击进入聊天室:

把整个流程串联了起来。

在好友列表点击聊天,会查询 userId 和 friendId 所在的一对一聊天室的 id(如果没查到,会创建一个),然后跳转到聊天页面,选中对应的聊天室。

群聊列表则是直接有 chatroomId。

然后实现了发送表情、图片、文件的功能:

表情用 emoji-mart 这个包实现。

图片就是之前的上传图片,只是上传完把 url 作为消息发过去,设置下 type 为 image。

文件也是一样上传,上传完把 url 作为消息发过去,设置 type 为 file。

然后展示 image 和 file 的时候分别作为图片展示,以及支持下载。

最后实现了收藏功能,双击聊天记录的时候调用 /favorite/add 添加收藏,参数是聊天记录 id:

然后结合当前登录用户的 userId,就生成了一条收藏:

这样,聊天室的前后端就都开发完了。

这个项目主要是练习 WebSocket 的,但也不是纯粹的 WebSocket,我们把它和之前学的关系型数据库结合了起来,实现了一个完整的系统。

项目重点有两个: 如何基于 WebSocket 实现聊天功能,如何把聊天功能和关系型数据库结合,也就是结合登录、好友、群聊、收藏等一系列功能。

微信、qq 等聊天软件它们也不是存粹的 WebSocket 聊天,也是和数据库结合了起来。

经过这个项目,你知道 WebSocket 如何在项目里用了么?