前端部分

游客

var myId = '';
var fenpei_info = '';
// 假定 随机游客ID

myId = parseInt(Math.random() * (999999 - 100000 + 1) + 100000);

function getUrlParam(id) {
    var regExp = new RegExp('([?]|&)' + id+ '=([^&]*)(&|$)');
    var result = window.location.href.match(regExp);
    if (result) {
        return decodeURIComponent(result[2]);
    } else {
        return null;
    }
};

var thisURL = document.location.href;

// 如果 url中包含这个参数
if (thisURL.indexOf('uid=') > 0)
{
    // 包含uid参数 赋值myId   游客id不变
    var v = getUrlParam('uid');
    myId = v;
}
else
{
    if (thisURL.indexOf("?") > 0) 
    {
        thisURL = thisURL + "&" + "uid=" + myId;
    }
    else 
    {
        thisURL = thisURL + "?" + "uid=" + myId;
    };
      location.href = thisURL;
};

// 如果 url中包含这个参数
if (thisURL.indexOf('kfid=') > 0)
{   
    // 包含kfid参数 赋值fenpei_info   客服id不变
    var v = getUrlParam('kfid');
    fenpei_info = v;
}
else
{
    // 分配客服
    $.ajax({
        url:"/api/Chat/assignCustomerService",
        type:'POST',
        async:false,
        data:{
            url : 'baidu.com',
            toid : myId
        },
        success:function(res){
            console.log(res);
            fenpei_info = res.data;
        }
    });
    if (thisURL.indexOf("?") > 0) 
    {
        thisURL = thisURL + "&" + "kfid=" + fenpei_info.data;
    }
    else 
    {
        thisURL = thisURL + "?" + "kfid=" + fenpei_info.data;
    };
    location.href = thisURL;
};

// ws
var ws = new WebSocket('ws://ip:端口');
ws.onopen = function(){
    // 首次连接
};
ws.onmessage = function(e){
    // json数据转换成js对象
    var data = eval("("+e.data+")");
    var type = data.type || '';
    console.log("服务端消息:");
    console.log(data);
    switch(type)
    {
        case 'init':
            var sendBind = '{"type":"bind","myid":"'+myId+'","url":"baidu.com"}';
            sendBind = eval('(' + sendBind + ')');
            $.ajax({
                url:"/api/Wsapi/bindUid",
                type:'POST',
                  async:false,
                data:{
                    client_id: data.client_id,
                    message: sendBind
                },
                success:function(res){
                    console.log('bindUid:');
                    console.log(res);
                }
            });
            var sendJoin = '{"type":"join","toid":"'+fenpei_info+'","myid":"'+myId+'"}';
            sendJoin = eval('(' + sendJoin + ')');
            $.ajax({
                url:"/api/Wsapi/joinGroup",
                type:'POST',
                data:{
                    client_id: data.client_id,
                    message: sendJoin
                },
                success:function(res){
                      console.log("joinGroup-returnData:");
                    console.log(res);
                }
            });
            break;
        case 'msg':
            if( data.status != 1 )
            {
                // 接收消息 显示
                console.log(data.message);
                var div = "<div class='msg_row'><img src='__IMG__/a.jpg' class='img'><span class='msg_span'>"+ data.message +"</span></div>";

                var id = $("button[name='btn']").attr("_id");
                $(".r_top[_id='"+id+"']").append(div);

                // 修改缩略框信息
                $(".people[_id='"+ id +"']").children(".msg").children(".msg_c").html(data.message);
            }
            else
            {
                return false;
            };
                
            break;
        default :
            // Layer.alert(e.data);
            return false;
    };
};
//断开 web socket 连接成功触发事件
ws.onclose = function () {
    console.log("连接已关闭...");
};

function sendMsg (){
    var content = $("input[name='content']").val();
    if(content == ""){
        Layer.alert("message is null");
        return false;
    };

    // var arr = '{"content":"'+content+',myId":"'+myId+',t_Id":"'+t_Id+'"}';
    var t_id = {$data.user_id};//分配的客服ID

    var send_data = '{"type":"user-admin","toid":"'+t_id+'","content":"'+content+'","myid":"'+myId+'","msg_type":"msg"}';
    // 接口形式发送消息
    send_data = eval('(' + send_data + ')');
    $.ajax({
        url:"/api/Wsapi/sendMsg",
        type:'POST',
        data:{
            message: send_data
        },
        success:function(res){
              console.log('sendMsg:');
            console.log(res);
        }
    });
    // ws.send(send_data);
    console.log(send_data);
    $("input[name='content']").val('');//重置输入框

    // 添加信息
    var data = "<div class='msg_row_mine'><span class='msg_span'>"+ content +"</span><img src='__IMG__/a.jpg' class='img_mine'></div>";
    $("div[class='r_top']").append(data);

    // 修改缩略框信息
    var id = $("button[name='btn']").attr("_id");
    $(".people[_id='"+ id +"']").children(".msg").children(".msg_c").html(content);
};

//心跳
setInterval(function(){
    var heart = '{"type":"ping"}';
    console.log("心跳:"+heart);
    ws.send(heart);
},3000);

$("button[name='btn']").click(function(){
    sendMsg();
    return false;
});
$(window).keydown(function(event){
    if(event.keyCode == 13) {
        sendMsg();
        return false;
    };
});

// 对话框点击事件
$(".people").click(function(){
    console.log("click");
    $(".r_top").hide();
    var id = $(this).attr("_id");
    $(".r_top[_id='"+id+"']").show();
    $(".btn").attr("_id",id);
});
// 发送信息
function run() { 
    var formData = new FormData(); 
    var file = document.getElementById("coverPhoto-file").files[0]; 
    formData.append("img", file); //这里的image是传给后端的字段,根据业务自行修改 
    formData.append("user", "1");
    formData.append("toid", "2");
    formData.append("mes", "1");
    formData.append("addtime", 1650000000);
    formData.append("mobile", 18012341234);
    formData.append("type", 2);
    formData.append("status", 1);
    $.ajax({ 
        url:"/api/Savemsg/saveImg", 
        type:"POST", 
        data:formData, 
        processData : false, 
        contentType : false, 
        dataType : 'json', 
        async : false, 
        success : function (result) { 
              console.log('saveImg:');
              console.log(result);
            //成功后的回调事件 
            if(result.code == 200)
            {   
                var msg = '{"type":"user-admin","toid":"'+{$data.user_id}+'","content":"'+result.imgPath+'","myid":"'+myId+'","msg_type":"img"}'
                // 接口形式发送消息
                send_data = eval('(' + msg + ')');
                $.ajax({
                    url:"/api/Wsapi/sendMsg",
                    type:'POST',
                    data:{
                        message: send_data
                    },
                    success:function(res){
                      console.log('sendMsg:');
                        console.log(res);
                    }
                });
                // 添加信息
                var data = "<div class='msg_row_mine'><span class='msg_span'>"+ result.imgPath +"</span><img src='__IMG__/a.jpg' class='img_mine'></div>";
                $("div[class='r_top']").append(data);

                // 修改缩略框信息
                var id = $("button[name='btn']").attr("_id");
                $(".people[_id='"+ id +"']").children(".msg").children(".msg_c").html(result.imgPath);
            }
            else
            {
                console.log(result);
            };
        } 
    }) 
};

客服

var ws = new WebSocket('ws://ip:端口');
var divArr = new Array();
ws.onopen = function(){
    // console.log("首次连接");
};
ws.onmessage = function(e){
    // json数据转换成js对象
    var data = eval("("+e.data+")");
    var type = data.type || '';
    switch(type)
    {
        case 'init':
            var sendBind = '{"type":"bind","myid":"'+2+'"}';
            ws.send(sendBind);
            break;
        case 'creat':
            if( $.inArray(data.client_id, divArr) != -1 )
            {
                return false;
            }
            else
            {
                // 创建聊天窗口
                var div = "<div class='people' _id='"+ data.client_id +"'><div style='width: 100%;height: 60px;' name='click_t_id' t_id='"+data.client_id+"'><div class='img_d'><img src='' class='img'></div><div class='msg'><div class='msg_t'>游客"+data.client_id+"</div><div class='msg_c'></div></div></div></div>";
                $("div[name='add_div']").append(div);

                var msgList = "<div class='r_top' style='display:none;' _id='"+data.client_id+"'>";
                $("div[name='msgList']").prepend(msgList);

                // 添加进数组
                divArr.push(data.client_id);
                console.log("add success");
            };
            break;
        case 'msg':
            if( data.status != 2 )
            {   
                // 接收消息 显示
                var div = "<div class='msg_row'><img src='__IMG__/a.jpg' class='img'><span class='msg_span'>"+ data.message +"</span></div>";

                $(".r_top[_id='"+data.userId+"']").append(div);

                // 修改缩略框信息
            }
            else
            {   
                // 客服发的消息
                var my_id = $("input[name='my_id']").val();
                if(data.myId != my_id){
                    // 其他客服的消息
                    var my_avatar = $("input[name='my_avatar']").val();
                    var div = "<div class='msg_row_mine'><span class='msg_span'>" + data.message + "</span><img src='" +my_avatar +"' class='img_mine'></div>";
                    $(".r_top[_id='"+data.userId+"']").append(div);
                };
            };
            // 修改缩略框信息
            $(".people[_id='"+ data.userId +"']").children(".msg").children(".msg_c").html(data.message);
            break;
        default :
            // Layer.alert(e.data);
            return false;
    };
};
//断开 web socket 连接成功触发事件
ws.onclose = function () {
    console.log("连接已关闭...");
};

//心跳
setInterval(function(){
    var heart = '{"code":'+5005+',"data":'+1+'}';
    console.log("心跳:"+heart);
    ws.send(heart);
},10000);

$(document).ready(function(){
    // 头像
    var my_avatar = $("input[name='my_avatar']").val();
    var my_id = $("input[name='my_id']").val();
    
    //发送信息
    function sendMsg (){
        var content = $("input[name='content']").val();
        if(content == ""){
            Layer.alert("message is null");
            return false;
        };

        var t_id = $("#t_id").val();
        if( t_id != 0 )
        {
            var send_data = '{"type":"admin-user","toid":"' + t_id + '","content":"' + content + '","myid":"'+my_id+'"}';
            ws.send(send_data);
            // console.log(send_data);
            $("input[name='content']").val('');//重置输入框

            // 添加信息
            var data = "<div class='msg_row_mine'><span class='msg_span'>" + content + "</span><img src='" +my_avatar +"' class='img_mine'></div>";
            $(".r_top[_id='"+t_id+"']").append(data);

            // 修改缩略框信息
            var id = $("button[name='btn']").attr("_id");
            $(".people[_id='"+ id +"']").children(".msg").children(".msg_c").html(content);
        }
        else
        {   
            Layer.alert("请选择聊天对象");
                
            return false;
        }; 
    };

    

    //发送操作
    $("button[name='btn']").click(function(){
        sendMsg();
        return false;
    });
    $(window).keydown(function(event){
        if(event.keyCode == 13) {
            sendMsg();
            return false;
        };
    });
});

功能部分

分配客服功能

use GatewayClient\Gateway;

#注册地址
private $registerAddress = 'ip:端口';

/**
 * 引入GatewayClient\Gateway 类
 */
public function requireGateWayWorker()
{
    $rootPath = Env::get('root_path');
    require_once $rootPath . '../vendor/GatewayWorker/GatewayClient-master/Gateway.php';
    Gateway::$registerAddress = $this->registerAddress;
}

/**
 * 分配客服
 *
 * @ApiMethod (POST)
 * @param $url 站点地址 string
 * @param $toid 游客ID int
 */
public function assignCustomerService()
{   
    if (request()->isAjax()) {
        $param = $this->request->param();
        if( empty($param['url']) )
        { 
            $this->success("missing parameter","",203);
        };

        $this->requireGateWayWorker();

        $website = Db::name("website")->where("url",$param['url'])->find();//站点数据
        if( $website )
        {
            // 查询站点负责小组
            if( $website["group"] )
            {   
                // 有负责的小组
                $explodeGroup = explode(",", $website["group"]);
                $users = [];
                foreach ($explodeGroup as $key => $v) {
                    $res = Db::name("admingroup")->where("id",$v)->value("user");
                    $explodeRes = explode(",",$res);
                    foreach ($explodeRes as $k => $vo) {
                        array_push($users, $vo);
                    };
                };
                //所有负责的用户ID
                $users = array_unique($users);//去重
                $emptyArr = [];
                $users = array_merge($users,$emptyArr);//下标重排
                $onlineUidArr = Gateway::getAllUidList();//获取全局所有在线uid列表

                $onlineList = [];//所有负责该站点的在线的客服列表
                foreach ($users as $k => $v) {
                    if( in_array("user".$v, $onlineUidArr))
                    {
                        array_push($onlineList,$v);
                    };
                };

                if( $onlineList )
                {
                    if( in_array("user".$users[$website['current']], $onlineUidArr) && $website['current']+1 >= count($users) )
                    {   
                        // 分配的用户在线 重头计算下标
                        Db::name("website")->where("url",$param['url'])->update(["current"=>0]);
                        $data = $users[$website['current']];
                    }
                    else if( in_array("user".$users[$website['current']], $onlineUidArr) && $website['current']+1 < count($users) )
                    {   
                        // 分配的用户在线 只需要增长下标
                        Db::name("website")->where("url",$param['url'])->update(["current"=>$website['current']+1]);
                        $data = $users[$website['current']];
                    }
                    else if( !in_array("user".$users[$website['current']], $onlineUidArr) && $website['current']+1 >= count($users) )
                    {   
                        // 不在线 并且 要重头开始计算下标
                        $userKey = array_search($onlineList[0], $users);//返回 在线负责的客服列表中 第一个客服 在 本站点所有负责客服列表中的下标
                        Db::name("website")->where("url",$param['url'])->update(["current"=>$userKey+1]);
                        $data = $onlineList[0];
                    }
                    else if( !in_array("user".$users[$website['current']], $onlineUidArr) && $website['current']+1 < count($users) )
                    {   
                        // 不在线 只需要下标+1
                        $afterList = [];//大于 当前分配的 不在线用户的下标 用户数组 (后半段用户数组 如3在[1,2,3,4,5]中 后半段为[4,5])
                        foreach ($users as $k => $v) {
                            if( $k > $website['current'] )
                            {
                                array_push($afterList,$v);
                            };
                        };

                        //对比在线列表
                        foreach ($afterList as $k => $v) {
                            if( in_array($v, $onlineList) )
                            {
                                // 后半数组中有在线的 从下一个在线的开始
                                $userKey = array_search($v, $users);
                                if( $userKey + 1 >= count($users) )
                                {
                                    // 此为最后一个在线用户 重头计算
                                    $userKey = 0;
                                }
                                else
                                {
                                    // 正常加 1
                                    $userKey = $userKey + 1;
                                };
                                Db::name("website")->where("url",$param['url'])->update(["current"=>$userKey]);
                                $data = $v;
                                break;
                            };
                        };
                        if( !isset($data) )
                        {
                            // 后半数组中没有在线的 从在线列表中第一个开始
                            $userKey = array_search($onlineList[0], $users);//返回 在线负责的客服列表中 第一个客服 在 本站点所有负责客服列表中的下标
                            Db::name("website")->where("url",$param['url'])->update(["current"=>$userKey+1]);
                            $data = $onlineList[0];
                        };
                    }
                    else
                    {
                        $this->success("Unknown error",null,203);
                    };
                }
                else
                {
                    // 当前负责的客服全部是离线  全部离线-机器人回复
                    $this->success("All offline",null,203);
                };
            }
            else
            {   
                // 无负责小组
                $this->success("No responsible team",null,203);
            };
        }
        else
        {
            $this->success("No website info",null,203);
        };

        //data:分配的用户ID;room:当前用户所连接的房间数组;max:最大连接数;online:负责该站点的所有在线客服ID数组
        $result = ["data"=>$data,"room"=>null,"max"=>null,"online"=>$onlineList];

        // 客服当前连接
        if($data)
        {   
            // 最大接待数量
            $result["max"] = Db::name("admin")->where("id",$data)->value("admit_number");
            // 当前连接数
            $clientIdArr = Gateway::getClientIdByUid("user".$data);
            $session = Gateway::getSession($clientIdArr[0]);//根据客服uid获取客服client_id然后获取设置的session
            $result["room"] = $session["room"];
              // 客服自动回复
            $map = [];
            $map["user"]  = ["=",$data];
            $map["url"]   = ["=",$website["id"]];
            $map["state"] = ["=",2];//1:关闭;2:启用
            $autoreply = Db::name("autoreply")->where($map)->find();
            $defaultMes = "你好";
            if( $autoreply )
            {
                $defaultMes = $autoreply["content"];
            };
            // 生成默认聊天数据
            $info = [
                'user'      => $data,
                'toid'      => $param["toid"],
                'mes'       => $defaultMes,
                'send_id'   => "kf",
                'addtime'   => time(),
                'type'      => 1,
                'status'    => 1,
            ];
            Db::name("message")->insert($info);
        };

        $this->success("success",$result,200);
    };
}

获取聊天列表

/**
 * 获取聊天列表
 * 
 * @ApiMethod (POST)
 * @param $userId 客服ID int
 */
public function getChatList()
{   
    if (request()->isajax()) {
        $param = $this->request->param();
        if( empty($param['userId']) )
        { 
            $this->success("missing parameter","",203);
        };

        $where = [];
        $where["user"] = ["=",$param['userId']];
        $data = Db::name("message")
                        ->field("user,toid,mes,send_id,type,status,addtime,mobile")
                        ->where($where)
                        ->order("addtime desc")
                        ->buildSql();
        $data = Db::table($data . ' t')->group("t.toid")->select();

        $this->requireGateWayWorker();
        // 获取客服client_id
        $admin_client_id = Gateway::getClientIdByUid("user".$param['userId']);

        //返回结果
        $result = [
                    "myList" => [],  //自己的聊天列表
                    "onlineList"=>[] //有权限查看并在线的房间ID列表
                ];

        if( $data )
        {   
            if( $admin_client_id )
            {   
                foreach ($data as $k => $v) {
                    $arr = [];
                    $arr = Gateway::isUidOnline($v["toid"]);//判断uid是否在线
                    $this_user = [];
                    
                    if( $arr )
                    {   
                        // 游客仍在线 执行加入
                        $user_client_id = Gateway::getClientIdByUid($v["toid"]);//游客client_id
                        Gateway::joinGroup($user_client_id[0], $v["toid"]);//以游客ID为房间名创建房间
                        Gateway::joinGroup($admin_client_id[0],$v["toid"]);//客服加入房间
                        // 配置Session
                        $session = '';
                        $session = Gateway::getSession($admin_client_id[0]);//获取设置的session
                        if( $session )
                        {   
                            if( !in_array($v["toid"],$session["room"]) )
                            {   
                                array_push($session["room"], $v["toid"]);
                                Gateway::updateSession( $admin_client_id[0] , array('room'=>$session["room"]) );
                            };
                        };
                        $mes = Db::name("message")->where("toid",$v["toid"])->order("id","desc")->find();
                        // 未读信息数量返回
                        $where = [];
                        $where["toid"] = ["=",$v["toid"]];
                        $where["status"] = ["=",0];//状态 0:未读
                        $unread = Db::name("message")->where($where)->count();
                        // 发送creat至前端 执行添加列表操作
                        Gateway::sendToGroup($v["toid"],json_encode(array(
                            "type"      => 'creat',
                            'client_id' => $v['toid'],
                            'last'      => $mes["mes"],
                            'last_type' => $mes["type"]
                        )));
                        // 最后一条聊天记录返回前端预览
                        $this_user = [
                            "id"        =>  $v["toid"],
                            "isOnline"  =>  1,
                            "lasttime"  =>  $v["addtime"],
                            "last"      => $mes["mes"],
                            "last_type" => $mes["type"],
                            "unread"    => $unread
                        ];
                        array_unshift($result["myList"], $this_user);
                        continue;
                    };
                    $this_user = [
                        "id"        =>  $v["toid"],
                        "isOnline"  =>  0,
                        "lasttime"  =>  $v["addtime"],
                        "last"      =>  "",
                        "last_type" =>  "1"
                    ];
                    array_push($result["myList"], $this_user);
                };

            }
            else
            {
                $this->success("Admin client_id is undefind",null,203);
            };
        };

        // 排序列表
        if( $result["myList"] )
        {   
            array_multisort(
                array_column($result["myList"],"isOnline"),SORT_DESC,
                array_column($result["myList"],"lasttime"),SORT_DESC,
                $result["myList"]
            );
              // 截取
            $sub_num = 20;//截取数量
            if( count($result["myList"]) > $sub_num )
            {   
                $pos = array_search(0, array_column($result["myList"], 'isOnline'));//0 第一次出现的下标值
                if( $pos == "" )
                {   
                    // 全部为在线 直接全部返回
                    $pos = count($result["myList"]);
                }
                else
                {
                    $pos = ($pos + 5 > count($result["myList"])) ? count($result["myList"]) : $pos + 5;
                };
                $result["myList"] = array_slice($result["myList"],0,$pos);
            };
        };

        // 返回 有权限查看并在线的房间列表
        $all_session = Gateway::getAllClientSessions();
        $admin_session = Gateway::getSession($admin_client_id[0]);//获取当前客服的session
        foreach ($all_session as $k => $v) {
            if( isset($v["user_type"]) )
            {
                if( $v["user_type"] == "user" )
                {   
                    // 筛选为游客
                    if( in_array($v["website"] , $admin_session["website"]) && !in_array($v["uid"], $result["onlineList"]) )
                    {   
                        // 当前游客的站点包含于本客服负责的站点
                        array_push($result["onlineList"], $v["uid"]);
                    };
                };
            };
        };

        $this->success("success",$result,200);
    };
}

客服解散房间

/**
 * 客服解散房间
 *
 * @param $roomId //房间ID
 * @return string|\think\response\Json
 */
public function disbandedRoom($roomId)
{
    if (request()->isajax()) {
        if( empty($roomId) )
        { 
            $this->success("missing parameter","",203);
        };
        $this->requireGateWayWorker();

        // 更新session
        $group_client_id_list = Gateway::getClientIdListByGroup($roomId);//获取小组里所有的client_id,包含游客/客服
        if( $group_client_id_list )
        {
            foreach ($group_client_id_list as $k => $v) {
                $session = "";
                $session = Gateway::getSession($v);//获取当前用户session
                if( $session["room"] )
                {   
                    // 有房间数据  更新房间session
                    $searchRes = "";
                    $searchRes = array_search($roomId,$session["room"]);
                    unset($session['room'][$searchRes]);
                    Gateway::updateSession($v, $session);
                };
                    
            };
        };

        // 解散房间 并通知游客
        Gateway::sendToGroup($roomId, json_encode(array(
            "type"    => "AdminUnsetRoom",  //类别标识 用于前端操作判断。此处操作应为发送event消息通知游客,客服已结束会话,并且无法继续发送消息/图片等操作
            "roomId"  => $roomId
        )));
        Gateway::ungroup($roomId);

        $this->success("success","",200);      
    };
}

将用户拉入房间

/**
 * 将用户拉入房间
 *
 * @param $client_id //用户全局唯一的client_id
 * @param $message   //消息 '{"type":"join","group":"xxxxx"}'
 * type join
 * @return string|\think\response\Json
 */
public function joinGroup($client_id,$message)
{
    if (request()->isajax()) {
        if( empty($client_id) || empty($message))
        { 
            $this->success("missing parameter","",203);
        };
        $this->requireGateWayWorker();

        if( $message['type'] == 'join' )
        {
            /*
            *   信息格式: '{"type":"join","toid":"负责该站点的所有在线客服ID: '1','2','...'","myid":"游客ID"}'
            */
            Gateway::joinGroup($client_id, $message["myid"]);//以游客ID为房间名创建房间
            $toid = explode(",",$message['toid']);   //客户端传来的客服ID
            // 负责该站点的所有在线客服 加入房间
            foreach ($toid as $k => $v) {
                $arr = Gateway::getClientIdByUid("user".$v);//根据客服uid获取客服client_id
                if( $arr )
                {
                    Gateway::joinGroup($arr[0],$message["myid"]);//客服加入房间
                    // 配置setSession
                    $session = '';
                    $session = Gateway::getSession($arr[0]);//获取设置的session
                    if( $session )
                    {   
                        if( !in_array($message["myid"],$session["room"]) )
                        {   
                            array_push($session["room"], $message["myid"]);
                            Gateway::updateSession($arr[0], array('room'=>$session["room"]));
                        };
                    };
                }; 
            };
            $all_session = Gateway::getAllClientSessions();
            $user_session = Gateway::getSession($client_id);//获取当前游客的session
            foreach ($all_session as $k => $v) {
                if( isset($v["user_type"]) )
                {
                    if( $v["user_type"] == "admin" )
                    {   
                        // 筛选为客服
                        if( in_array($user_session["website"], $v["website"]) )
                        {   
                            // 当前游客的站点包含于该客服负责的站点 则返回信息添加列表内容
                            Gateway::sendToUid($v['uid'], json_encode(array(
                                "type" => 'creatList',
                                'client_id' => $message['myid']
                            )));
                        };
                    };
                };
            };

            $mes = Db::name("message")->where("toid",$message['myid'])->order("id","desc")->find();
            Gateway::sendToGroup($message["myid"],json_encode(array(
                "type" => 'creat',
                'client_id' => $message['myid'],
                'last' => $mes["mes"],
                'last_type' => $mes["type"]
            )));

            $this->success("success","",200);
        }
        else{
            $this->success("error","",203);
        };
    };
}

绑定Uid

/**
 * 绑定Uid
 *
 * @param $client_id 
 * @param $message
 * type   bind
 * @return string|\think\response\Json
 */
public function bindUid($client_id, $message)
{   
    if (request()->isajax()) {
        if( empty($client_id) || empty($message))
        { 
            $this->success("missing parameter","",203);
        };
        // $message = '{"type":"bind","myid":"游客为ID/客服为"user"+ID","url":"域名"}'
        $this->requireGateWayWorker();

        if( $message['type'] == 'bind' )
        {
            Gateway::bindUid($client_id, $message["myid"]);//操作绑定 游客为ID/客服为"user"+ID

            $explodeRes = explode("user", $message["myid"]);
            if( count($explodeRes) > 1 )
            {
                // 客服
                $allGroup = Db::name("admingroup")->select();//所有小组
                $user_group = [];//客服所在小组
                foreach ($allGroup as $k => $v) {
                    $ex = [];
                    $ex = explode(",", $v["user"]);
                    if( in_array($explodeRes[1], $ex) )
                    {
                        array_push($user_group, $v["id"]);
                    };
                };

                $allWebsite = Db::name("website")->field("id,group")->select();//所有站点
                $user_website = [];//客服所负责站点ID
                foreach ($allWebsite as $k => $v) {
                    $ex = [];
                    $ex = explode(",", $v["group"]);
                    if( in_array($explodeRes[1], $ex) )
                    {
                        array_push($user_website, $v["id"]);
                    };
                };

                // 配置session
                Gateway::setSession($client_id, array(
                    'uid'       =>  $message["myid"],
                    'client_id' =>  $client_id,
                    'room'      =>  [],
                    'user_type' =>  "admin",
                    'website'   =>  $user_website   //站点
                ));
            }
            else
            {
                // 游客
                $website = Db::name("website")->where("url",$message["url"])->find();
                // 配置session
                Gateway::setSession($client_id, array(
                    'uid'       =>  $message["myid"],
                    'client_id' =>  $client_id,
                    'room'      =>  [],
                    'user_type' =>  "user",
                    'project'   =>  $website["p_id"],  //项目
                    'region'    =>  $website["r_id"],  //地区
                    'website'   =>  $website["id"]     //站点

                ));
            };

            $this->success("success",Gateway::getSession($client_id),200);
        }
        else{
            $this->success("error","",203);
        };
    };
}

发送消息

/**
 * 发送消息
 *
 * @param $message
 * type   sendMsg
 * @return string|\think\response\Json
 */
public function sendMsg($message)
{   
    if (request()->isajax()) {
        if( empty($message))
        { 
            $this->success("missing parameter","",203);
        };
        // $message = '{"type":"bind","myid":"游客为ID/客服为"user"+ID"}'
        $this->requireGateWayWorker();

        if( $message['type'] == 'user-admin' )
        {
            //游客发消息
            /*
            *   信息格式: '{"type":"user-admin","toid":"分配的客服ID","content":"content","myid":"游客ID","msg_type":"消息类型"}'
            */
            if( Gateway::isUidOnline("user".$message["toid"]) )
            {   
                $toid = $message['myid'];   //客户端传来的游客ID,也是房间ID
                if( Gateway::getUidCountByGroup($toid) )
                {   
                    // 向指定房间内所有人发送消息
                    Gateway::sendToGroup($toid, json_encode(array(
                        "type"     => "msg",
                        "message"  => $message["content"],
                        "userId"   => $toid,
                        "msg_type" => $message["msg_type"],
                        "myId"     => $message["toid"],
                        "status"   => 1
                    )));
                };
                $this->success("success","",200);
            }
            else
            {
                $this->success("Offline","",206);
            };
                
        }
        else if( $message['type'] == 'admin-user' )
        {
            //客服发消息
            /*
            *   信息格式: '{"type":"admin-user","toid":"游客ID/也是房间ID","content":"content","myid":"客服ID","msg_type":"消息类型"}'
            */
            if( Gateway::isUidOnline($message["toid"]) )
            {
                $toid = $message['toid'];   //客户端传来的目标游客id/也是房间id
                if( Gateway::getUidCountByGroup($toid) )
                {   
                    // 向指定房间内所有人发送消息
                    Gateway::sendToGroup($toid, json_encode(array(
                        "type"     => "msg",
                        "message"  => $message["content"],
                        "userId"   => $toid,
                        "msg_type" => $message["msg_type"],
                        "myId"     => $message["myid"],
                        "status"   => 2
                    )));
                };
                $this->success("success","",200);
            }
            else
            {
                $this->success("Offline","",206);
            };
                
        }
        else{
            $this->success("error","",203);
        };
    };
}

销毁session房间号

/**
 * 销毁session房间号
 *
 * @param $client_id
 * @param $message
 * type   unsetRoom
 * @return string|\think\response\Json
 */
public function unsetRoom($client_id, $message)
{   
    if (request()->isAjax()) {
        if( empty($client_id) || empty($message))
        { 
            $this->success("missing parameter","",203);
        };
        $this->requireGateWayWorker();

        if( $message['type'] == 'unsetRoom' )
        {
            /*
            *   信息格式: '{"type":"unsetRoom","roomId":"roomId"}';
            */
            $session = Gateway::getSession($client_id);
            $searchRes = array_search($message["roomId"],$session["room"]);
            unset($session['room'][$searchRes]);
            Gateway::updateSession($client_id, $session);
            $data = Gateway::getSession($client_id);

            $this->success("success",$data,200);
        }
        else{
            $this->success("error","",203);
        };
    };
}

Events部分

/**
 * 当客户端连接时触发
 * 如果业务不需此回调可以删除onConnect
 * @param int $client_id 连接id
 */
public static function onConnect($client_id)
{   

    var_dump('type:init'  . "client_id:" . $client_id);
    Gateway::sendToClient($client_id, json_encode(array(
        'type'      => 'init',
        'client_id' => $client_id,
        'from' => 'ip'
    )));
 
  
}

/**
 * 当客户端发来消息时触发
 * @param int $client_id 连接id
 * @param string $message 具体消息
*/
public static function onMessage($client_id, $message)
{   
    var_dump('client_id:' . $client_id . "message" . $message);
}

/**
 * 当用户断开连接时触发
 * @param int $client_id 连接id
*/
public static function onClose($client_id)
{    
    $uid = $_SESSION['uid'];//返回client_id绑定的uid  游客为ID/客服为"user"+ID
    $explodeRes = explode("user", $uid);
    if( count($explodeRes) > 1 )
    {
        // 客服
        if( $_SESSION["room"] )
        {
            // 存在当前连接数session
            foreach ($_SESSION["room"] as $k => $v) {
                $onlineNum = 0;
                $onlineNum = Gateway::getClientIdCountByGroup($v);//获取分组当前在线成员连接数(不包含退出连接的当前用户)
                if( $onlineNum == 1)
                {
                    // 无其余客服在线
                    var_dump("解散房间");
                    Gateway::sendToGroup($v, json_encode(array(
                        "type"    => "dissolution"
                    )));
                    Gateway::ungroup($v);//解散房间
                }
                  else
                {
                    Gateway::sendToGroup($v, json_encode(array(
                        "type"    => "adminOffline"
                    )));    
                };
            };
        };
    }
    else
    {   
        // 删除当前游客房间内 所有客服中$_SESSION['room']的该房间号
        Gateway::sendToGroup($uid, json_encode(array(
            "type"    => "unsetRoom",
            "roomId"  => $uid
        )));
        // 游客
        Gateway::ungroup($uid);//解散房间
    };

}