夜猫子的知识栈 夜猫子的知识栈
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

夜猫子

前端练习生
首页
  • 前端文章

    • JavaScript
  • 学习笔记

    • 《JavaScript教程》
    • 《Web Api》
    • 《ES6教程》
    • 《Vue》
    • 《React》
    • 《TypeScript》
    • 《Git》
    • 《Uniapp》
    • 小程序笔记
    • 《Electron》
    • JS设计模式总结
  • 《前端架构》

    • 《微前端》
    • 《权限控制》
    • monorepo
  • 全栈项目

    • 任务管理日历
    • 无代码平台
    • 图书管理系统
  • HTML
  • CSS
  • Nodejs
  • Midway
  • Nest
  • MySql
  • 其他
  • 技术文档
  • GitHub技巧
  • 博客搭建
  • Ajax
  • Vite
  • Vitest
  • Nuxt
  • UI库文章
  • Docker
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Node基础

  • 《MySQL》学习笔记

  • Midway

  • Nest

    • 开篇词
    • 学习理由
    • nest概念扫盲
    • 快速掌握 nestcli
    • 5种http数据传输方式
    • IoC 解决了什么痛点问题?
    • 如何调试 Nest 项目
    • Provider注入对象
    • 全局模块和生命周期
    • AOP 架构有什么好处?
    • 一网打尽 Nest 全部装饰器
    • Nest如何自定义装饰器
    • Metadata和Reflector
    • ExecutionContext切换上下文
    • Module和Provider的循环依赖处理
    • 如何创建动态模块
    • Nest和Express,fastify
    • Nest的Middleware
    • RxJS和Interceptor
    • 内置Pipe和自定义Pipe
    • ValidationPipe验证post请求参数
    • 如何自定义 Exception Filter
    • 图解串一串 Nest 核心概念
    • 接口如何实现多版本共存
    • Express如何使用multer实现文件上传
    • Nest使用multer实现文件上传
    • 图书管理系统
    • 大文件分片上传
    • 最完美的 OSS 上传方案
    • Nest里如何打印日志
    • 为什么Node里要用Winston打印日志
    • Nest 集成日志框架 Winston
    • 通过Desktop学Docker也太简单了
    • 你的第一个 Dockerfile
    • Nest 项目如何编写 Dockerfile
    • 提升 Dockerfile 水平的 5 个技巧
    • Docker 是怎么实现的
    • 为什么 Node 应用要用 PM2 来跑?
    • 快速入门 MySQL
    • SQL 查询语句的所有语法和函数
    • 一对一、join 查询、级联方式
    • 一对多、多对多关系的表设计
    • 子查询和 EXISTS
    • SQL 综合练习
    • MySQL 的事务和隔离级别
    • MySQL 的视图、存储过程和函数
    • Node 操作 MySQL 的两种方式
    • 快速掌握 TypeORM
    • TypeORM 一对一的映射和关联 CRUD
    • TypeORM 一对多的映射和关联 CRUD
    • TypeORM 多对多的映射和关联 CRUD
    • 在 Nest 里集成 TypeORM
    • TypeORM保存任意层级的关系
    • 生产环境为什么用TypeORM的migration迁移功能
    • Nest 项目里如何使用 TypeORM 迁移
    • 如何动态读取不同环境的配置?
    • 快速入门 Redis
    • 在 Nest 里操作 Redis
    • 为什么不用 cache-manager 操作 Redis
    • 两种登录状态保存方式:JWT、Session
    • Nest 里实现 Session 和 JWT
    • MySQL + TypeORM + JWT 实现登录注册
    • 基于 ACL 实现权限控制
    • 基于 RBAC 实现权限控制
    • access_token和refresh_token实现无感登录
    • 单token无限续期实现登录无感刷新
    • 使用 passport 做身份认证
    • passport 实现 GitHub 三方账号登录
    • passport 实现 Google 三方账号登录
    • 为什么要使用 Docker Compose ?
    • Docker 容器通信的最简单方式:桥接网络
    • Docker 支持重启策略,是否还需要 PM2
    • 快速掌握 Nginx 的 2 大核心用法
    • 基于 Nginx 实现灰度系统
    • 基于 Redis 实现分布式 session
    • Redis + 高德地图,实现附近的充电宝
    • 用 Swagger 自动生成 api 文档
    • 如何灵活创建 DTO
    • class- validator 的内置装饰器,如何自定义装饰器
    • 序列化 Entity,你不需要 VO 对象
    • 手写序列化 Entity 的拦截器
    • 使用 compodoc 生成文档
    • Node 如何发邮件?
    • 实现基于邮箱验证码的登录
    • 基于 sharp 实现 gif 压缩工具
    • 大文件如何实现流式下载?
    • Puppeteer 实现爬虫,爬取 BOSS 直聘全部前端岗位
    • 实现扫二维码登录
    • Nest 的 REPL 模式
    • 实现 Excel 导入导出
    • 如何用代码动态生成 PPT
    • 如何拿到服务器 CPU、内存、磁盘状态
    • Nest 如何实现国际化?
    • 会议室预订系统:需求分析和原型图
    • 会议室预订系统:技术方案和数据库设计
    • 会议室预订系统:用户管理模块--用户注册
    • 会议室预订系统:用户管理模块--配置抽离、登录认证鉴权
    • 会议室预订系统:用户管理模块-- interceptor、修改信息接口
    • 会议室预订系统:用户管理模块--用户列表和分页查询
    • 会议室预订系统:用户管理模块-- swagger 接口文档
    • 会议室预订系统:用户管理模块-- 用户端登录注册页面
    • 会议室预订系统:用户管理模块-- 用户端信息修改页面
    • 会议室预订系统:用户管理模块-- 头像上传
    • 会议室预订系统:用户管理模块-- 管理端用户列表页面
    • 会议室预订系统:用户管理模块-- 管理端信息修改页面
    • 会议室预订系统:会议室管理模块-后端开发
    • 会议室预订系统:会议室管理模块-管理端前端开发
    • 会议室预订系统:会议室管理模块-用户端前端开发
    • 会议室预订系统:预定管理模块-后端开发
    • 会议室预订系统:预定管理模块-管理端前端开发
    • 会议室预订系统:预定管理模块-用户端前端开发
    • 会议室预订系统:统计管理模块-后端开发
    • 会议室预订系统:统计管理模块-前端开发
    • 会议室预订系统:后端项目部署到阿里云
    • 会议室预订系统:前端项目部署到阿里云
    • 会议室预定系统:用 migration 初始化表和数据
    • 会议室预定系统:文件上传 OSS
    • 会议室预定系统:Google 账号登录后端开发
    • 会议室预定系统:Google 账号登录前端开发
    • 会议室预定系统:后端代码优化
    • 会议室预定系统:集成日志框架 winston
    • 会议室预定系统:前端代码优化
    • 会议室预定系统:全部功能测试
    • 会议室预定系统:项目总结
    • Nest 如何创建微服务?
    • Nest 的 Monorepo 和 Library
    • 用 Etcd 实现微服务配置中心和注册中心
    • Nest 集成 Etcd 做注册中心、配置中心
    • 用 Nacos 实现微服务配置中心和注册中心
    • 基于 gRPC 实现跨语言的微服务通信
    • 快速入门 ORM 框架 Prisma
    • Prisma 的全部命令
    • Prisma 的全部 schema 语法
    • Primsa Client 单表 CRUD 的全部 api
    • Prisma Client 多表 CRUD 的全部 api
    • 在 Nest 里集成 Prisma
    • 为什么前端监控系统要用 RabbitMQ?
    • 基于 Redis 实现关注关系
    • 基于 Redis 实现各种排行榜(周榜、月榜、年榜)
    • 考试系统:需求分析
    • 考试系统:技术方案和数据库设计
    • 考试系统:微服务、Lib 拆分
    • 考试系统;用户注册
    • 考试系统:用户登录、修改密码
    • 考试系统:考试微服务
    • 考试系统:登录、注册页面
    • 考试系统:修改密码、试卷列表页面
    • 考试系统:新增试卷、回收站
    • 考试系统:试卷编辑器
    • 考试系统:试卷回显、预览、保存
    • 考试系统:答卷微服务
    • 考试系统:答题页面
    • 考试系统:自动判卷
    • 考试系统:分析微服务、排行榜页面
    • 考试系统:整体测试
    • 考试系统:项目总结
    • 用 Node.js 手写 WebSocket 协议
    • Nest 开发 WebSocket 服务
    • 基于 Socket.io 的 room 实现群聊
    • 聊天室:需求分析和原型图
    • 聊天室:技术选型和数据库设计
    • 聊天室:用户注册
    • 聊天室:用户登录
    • 聊天室:修改密码、修改信息
    • 聊天室:好友列表、发送好友申请
    • 聊天室:创建聊天室、加入群聊
    • 聊天室:登录、注册页面开发
    • 聊天室:修改密码、信息页面开发
    • 聊天室:头像上传
    • 聊天室:好友∕群聊列表页面
    • 聊天室:添加好友弹窗、通知页面
    • 聊天室:聊天功能后端开发
    • 聊天室:聊天功能前端开发
    • 聊天室:一对一聊天
    • 聊天室:创建群聊、进入群聊
    • 聊天室:发送表情、图片、文件
    • 聊天室:收藏
    • 聊天室:全部功能测试
    • 聊天室:项目总结
    • MongoDB 快速入门
    • 使用 mongoose 操作 MongoDB 数据库
    • GraphQL 快速入门
    • Nest 开发 GraphQL 服务:实现 CRUD
    • GraphQL + Primsa + React 实现 TodoList
    • 如何调试 Nest 源码?
  • 其他

  • 服务端
  • Nest
神说要有光
2025-03-10

聊天室:项目总结

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

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

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

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

群聊里可以多人聊天。

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

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

image.png

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

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

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

首先,房间名存储的是 id

image.png

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

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

字段名 数据类型 描述
id INT 聊天室ID
name VARCHAR(50) 群聊名
type BOOLEAN 类型(单聊、群聊)
create_time DATETIME 创建时间
update_time DATETIME 更新时间

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

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

user_chatroom:

字段名 数据类型 描述
user_id INT 用户ID
chatroom_id INT 聊天室ID

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

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

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

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

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

image.png

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

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

image.png

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

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

image.png

image.png

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

image.png

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

image.png

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

image.png

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

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

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

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

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

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

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

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

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

数据库设计:

分析了下接口:

image.png

接下来进入开发:

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

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

image.png

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

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

image.png

从数据库迁移方面来说,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。

image.png

image.png

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

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

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

image.png

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

image.png

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

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

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

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

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

编辑 (opens new window)
上次更新: 2025/10/27 10:53:52
聊天室:全部功能测试
MongoDB 快速入门

← 聊天室:全部功能测试 MongoDB 快速入门→

最近更新
01
H5调用微信jssdk
09-28
02
VueVirtualScroller
09-19
03
如何调试 Nest 项目
03-10
更多文章>
Copyright © 2019-2025 Study | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式