🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 效果图: ![聊天](https://box.kancloud.cn/2aa2836f74b34193f1392443f16d5e91_792x535.png) ### 功能: - 好友私聊 - 多人群聊 - 添加好友 - 加入群组 - 离线消息 - 未读消息提醒 - 聊天对话窗口 ### 流程: - 客户端第一次连接服务端,服务端保存连接和uid映射关系 ![客户端第一次连接服务端,服务端保存连接和uid映射关系](https://box.kancloud.cn/73150371a18a3dca2f01738fe89a1652_596x203.png) - 主动发送消息给服务端,调用ws.send(),接受服务端发来消息时会触发onmessage事件 ![主动发送消息给服务端,调用ws.send(),接受服务端发来消息时会触发onmessage事件](https://box.kancloud.cn/bc2482302eb56efb30aea7c9fdd6b7f6_472x341.png) #### 客户端api ```javascript var ws = new WebSocket(); ws.onopen() // 第一次连接服务端,会触发 ws.onmessage() // 接受到服务端消息会触发 ws.send() // 主动推送消息给服务端 ws.onclose() // 关闭客户端会触发 ``` #### 服务端api ```php $server = new swoole_websocket_server() $server->on('open',functoin(){}); // 有客户端连接时,会触发 $server->on('message',function(){}) // 接受到客户端消息会触发 $server->on('close',function(){}) // 客户端关闭会触发,可以用来清除redis中fd和uid的映射关系 ``` ### 考虑的问题: - 离线消息和聊天历史记录问题: - 不在线的用户也可以收到消息,用户可以查看历史聊天记录,要实现这两个功能点必须要求消息持久化处理,可以保存到mysql,mongodb,file文件; - 消息可以分为两种类型,一种是私聊,另一种是群聊,保存的时候要区分两者 - 保存聊天记录不影响我们的正常流程,所以可以用异步去处理数据,这样系统可以更快的响应客户端 - 保存用户uid和连接fd映射关系: - 客户端每次打开新页面都会有产生一个新的连接fd,也就说一个uid会对应多个fd,发送给uid时,我们需要把消息推送到uid对应的每一个fd,这里用redis的集合来存储uid和fd关系 - 聊天对话: - 聊天对话是相互的,例如a给b发消息,此时a和b拥有同个对话,我们a和b对话设置成一条记录,找出对话框时可以用or来处理,避免生成两条记录,同时这个对话ID可以作为消息记录外键,a和b都可以通过对话 ID查询到彼此的聊天记录 ### 数据库设计: > 针对上面问题,我主要设置了聊天对话表和聊天记录表 - 对话框表 ### 表: chat_dialog 聊天对话表 | 字段 | 类型 | 备注 | | :------: | :------: | :------: | | id | int | 主键ID | | from_uid | int | 对话发起者 | | to_uid | int | 对话接受者 | | group_id | int | 群组ID,默认为0 | | type | tinyint | 对话类型,1表示人对人,2表示人对群 | | status | tinyint | 状态,0正常,1关闭 | 获取某个人所有对话(包括人对人,人对群): ```sql select * from chat_dialog where from_uid =1 or to_uid = 1; ``` > 说明下:例如a发消息给b,此时生成一条chat_dialog记录,from_uid是a,to_uid是b;需要获取a所有对话时,可以用select * from chat_dialog where from_uid = a or to_uid =a; 同理,需要获取b聊天对话时,可以执行select * from chat_dialog where from_uid = b or to_uid =b;这样双方都能获得同一个对话消息 ### 表: chat_records 消息记录表 | 字段 | 类型 | 备注 | | :------: | :------: | :------: | | id | int | 主键ID | | user_id | int | 消息发送者 | | dialog_id | int | 对话ID,包括私聊和群聊 | | group_id | int | 群组ID,默认为0 | | status | tinyint | 状态,0正常,1关闭 | 获取私聊对话历史消息: ```sql select * from chat_records where dialog_id = 1; ``` 获取群聊历史消息 ```sql select * from chat_records where group_id = 1; ```