laravel利用websocket搭建一个简单的实时聊天系统
主要实现功能就是用户之间的的实时聊天。
理一下业务逻辑:
用户A给用户B发送消息
>如果用户B在线,直接通过websocket推送消息,用户B接受到消息后写入前端数据库,并归档数据
>如果用户B不在线,则暂存进mysql数据库。
>用户B不在线,打开进入APP,利用接口拉取历史消息,并归档存储进入数据库
<?php /** * Author:陈杰 * Blog:http://blog.95shouyou.com * Email:823380606@qq.com * Git:https://gitee.com/chen95 * Date:2021/1/29 0029 * Time:12:24 */ namespace App\Http\Controllers\Member; use App\Http\Controllers\Controller; use App\Model\Member\MemberChatModel; use GatewayClient\Gateway; use Illuminate\Http\Request; class ChatController extends Controller { public function msg_list(Request $request) { $member_id = $request->get('member_id'); $data = MemberChatModel::suffix($member_id, 20) ->with("member:id,name,avatar") ->where("return_id", $member_id) ->get(); for ($i = 0; $i < count($data); $i++) { $data[$i]->delete(); } return self::success($data); } public function send(Request $request) { $rules = [ 'return_id' => 'required|integer|min:1', 'type' => 'required|integer|in:1,2,3,4', 'content' => 'required|array' ]; $params = self::checkValidate($rules, $request->all()); $data = [ 'member_id' => $request->get('member_id'), 'return_id' => $params['return_id'], 'type' => $params['type'], 'content' => $params['content'], 'create_time' => time(), ]; try { $is = Gateway::isUidOnline($params['return_id']); if ($is) { $msg = [ 'type' => 101, 'msg' => "ok", 'code' => 200, 'data' => $data, 'time' => time() ]; Gateway::sendToUid($params['return_id'], json_encode($msg)); } else { $model = new MemberChatModel(); $model->setSuffix($data['return_id'], 20)->fill($data); $model->save(); } return self::success($data); } catch (\Exception $exception) { return self::error($exception->getMessage()); } } }
理一下代码逻辑:
一个接口msg_list()就是我们的拉取历史消息,消息没有分类,需要前端自行归档。
接口send() 这里有一个发送人的id:member_id 和接收消息人的id:return_id。根据return_id取模做数据库分表。
因为用到的websocket是workerman,没有发送消息结果的回调,所以做了一个判断就是Gateway::isUinOnline,判断接收用户是否在线,在线的话就可以直接发送,不在线就暂存进数据库,等待用户打开APP自行拉取。
参数type决定发送的消息是文字,图片,视频等类型。
参数content是一个数组,例如是文字消息那么content={"text":"你好"}。如果是图片消息那么content={"url":"http://www.95shouyou.com"} 存储一个图片地址。。具体的跟前端同学自行商量就行了
看看Model
<?php /** * Author:陈杰 * Blog:http://blog.95shouyou.com * Email:823380606@qq.com * Git:https://gitee.com/chen95 */ namespace App\Model\Member; use App\Model\BaseModel; class MemberChatModel extends BaseModel { public $connection = "mysql_logs"; public $timestamps = false; public $table = "member_chat"; protected $casts = [ 'content' => 'array', ]; public $fillable = ["member_id", "return_id", "type", "content", "create_time", "is"]; public function member() { return $this->hasOne(MemberModel::class, 'id', 'member_id'); } public function return_member() { return $this->hasOne(MemberModel::class, 'id', 'member_id'); } // 设置表后缀 public function setSuffix(int $suffix, int $mold) { if ($suffix > 0) { $suffix = intval($suffix % $mold) + 1; $this->table = $this->getTable() . '_' . $suffix; } return $this; } }
数据库结构
# Host: www.95shouyou.com (Version: 5.7.32-log) # Date: 2021-02-02 15:37:14 # Generator: MySQL-Front 5.3 (Build 4.234) /*!40101 SET NAMES utf8 */; # # Structure for table "member_chat_1" # CREATE TABLE `member_chat_1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `member_id` int(11) NOT NULL DEFAULT '0' COMMENT '发送人的id', `return_id` int(11) NOT NULL DEFAULT '0' COMMENT '接收人的id', `type` tinyint(3) NOT NULL DEFAULT '0' COMMENT '1文字,2图片,3视频,4位置', `content` text NOT NULL COMMENT '消息内容', `create_time` int(11) NOT NULL DEFAULT '0' COMMENT '创建时间', `is` tinyint(3) NOT NULL DEFAULT '0' COMMENT '0:未读 1已读', PRIMARY KEY (`id`), KEY `member_id` (`member_id`), KEY `return_id` (`return_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户聊天记录';
数据库: shenyanshe_log
表格: member_chat_1
备注: 用户聊天记录
索引:
名称 | 类型 | 属性 | 备注 |
---|---|---|---|
主索引 | id | unique | |
member_id | member_id | ||
return_id | return_id |
字段:
名称 | 类型 | 空 | 默认值 | 属性 | 备注 |
---|---|---|---|---|---|
id | int(11) | 否 | <auto_increment> | ||
member_id | int(11) | 否 | 0 | 发送人的id | |
return_id | int(11) | 否 | 0 | 接收人的id | |
type | tinyint(3) | 否 | 0 | 1文字,2图片,3视频,4位置 | |
content | text | 否 | 消息内容 | ||
create_time | int(11) | 否 | 0 | 创建时间 | |
is | tinyint(3) | 否 | 0 | 0:未读 1已读 |
