Workerman&&GatewayWorker

xiaoxiao2025-09-01  7

需求:初步搭建服务器

服务器一台(linux)+自己安装PHP环境,有PHP环境就行了,apache,nginx,不影响

开始:

curl -Ss http://www.workerman.net/check.php | php

查看服务器是否有安装workerman的环境

如果出现这个情况就OK。一般都是posix没有。

安装posix

yum search php56w-process

查找此扩展

yum install php56w-process.x86_64

安装此扩展

php -m|grep posix

查看是否安装成功

再运行查看服务器是否有安装workerman的环境的命令,上面有。

 

安装GatewayWorker ,http://doc2.workerman.net/。官网地址。

安装到随便哪个目录都行,建议还是web目录最好。

然后按照官网方式启动。

重点:1.Events.php(在application/yourapp)主要逻辑编写在这个里面,另外三个是注册文件,启动文件,业务启动文件

2.start_gateway里面有默认端口。测试不用改,正式运营要改。我这里是做测试,默认8282,去阿里云服务器安全组开启此端口

3.开启phpstudy,新建一个index.html页面

<!DOCTYPE html> <html> <head> <title>demo</title> </head> <body> 1231 </body> <script type="text/javascript"> var ws = new WebSocket("ws://你的服务器IP地址:8282"); console.log(ws); ws.onopen = function(evt){ //绑定连接事件 ws.send("hello workerman!"); }; ws.onmessage = function(evt) {//绑定收到消息事件 console.log("Received Message: " + evt.data); }; </script> </html>

访问此地址,去服务器查看,就会有信息弹出。

每请求一次就会出现一个ID。

4.每当修改了一次GatewayWorker的代码,不管是var_dump或者echo了都要重新启动一次GatewayWorker服务。

php start.php restart

好了,大功告成。具体操作按照自己实际业务逻辑来就行。

events.php

<?php use \GatewayWorker\Lib\Gateway; use \GatewayWorker\Lib\Db; /** * 主逻辑 * 主要是处理 onConnect onMessage onClose 三个方法 * onConnect 和 onClose 如果不需要可以不用实现并删除 */ class Events { /** * 新建一个类的静态成员,用来保存数据库实例 */ public static $db = null; /** * 进程启动后初始化数据库连接 */ public static function onWorkerStart($worker) { //self::$db = new \Workerman\MySQL\Connection('120.79.57.160', '3306', 'root', 'TPshop@2018', 'tp_kefu');//线上 self::$db = new \Workerman\MySQL\Connection('127.0.0.1', '3306', 'root', 'root', 'b2t_im');//本地 } /** * 当客户端发来消息时触发 * @param int $client_id 连接id * @param mixed $message 具体消息 */ public static function onMessage($client_id, $data) { $message = json_decode($data, true); $message_type = $message['type']; //Events::setLogs(json_encode($message));//写日志 switch ($message_type) { case 'init': $uid = $message['id']; $_SESSION = array( 'username' => $message['username'], 'avatar' => $message['avatar'], 'id' => $uid, 'sign' => $message['sign'], ); // 将当前链接与uid绑定 Gateway::bindUid($client_id, $uid); $db1 = self::$db; //数据库链接 // 问题:如何区分客服和客户id? if (substr($uid,0,4) != 'kefu') { //客户发消息过来 $userid = $uid; $storeid = $message['storeid'] ? $message['storeid'] : 1; if(substr($uid,0,2) == 'hy'){ //如果是会员 修改上线状态 $hy = explode('_',$uid); $db1->update('tp_kf_custom')->cols(array('status'=>1))->where('uid = '.$hy[1])->query(); } //查找配置表 获取一条数据 $config = $db1->select('*')->from('tp_kf_config')->where('store_id = '.$storeid)->row(); if($config){ //存在配置 //匹配逻辑说明 1.优先匹配最近曾服务过的客服,不在线 匹配紧急联系人 $ownership = $db1->query('SELECT k.* FROM tp_kf_ownership c LEFT JOIN tp_kf_kefu k ON k.id=c.kefu_id WHERE c.client_id ="'.$userid.'" AND c.store_id ='.$storeid.' AND c.is_delete = 0 AND k.Auditing = 1 AND k.is_delete = 0 ORDER BY c.id desc LIMIT 1'); $kefu = array(); if($ownership){ //存在归属人 老客户 $ownership = $ownership[0]; if($ownership['status'] == 1){ //在线 $kefu = $ownership; }else{ //不在线 if($ownership['second_contact_id']){ //设置了紧急联系人 $second_contact = $db1->select('*')->from('tp_kf_kefu')->where('id ='.$ownership['second_contact_id'])->row(); if($second_contact && $second_contact['status'] == 1){ //紧急联系人在线 $kefu = $second_contact; }else{ //紧急联系人不在线 该客户离线消息 $kefu = $ownership; } }else{ //未设置紧急联系人 该客服的离线消息 $kefu = $ownership; } } }else{ //新客户 if($config['type'] == 1){ //随机匹配客服 //2.没有被客服接待过,随机匹配在线客服,木有,匹配机器人 $kefu_data = $db1->query('select * from tp_kf_kefu where storeid='.$storeid.' and status=1 and Auditing = 1 and is_delete = 0 order by rand() limit 1'); if($kefu_data){ //存在在线客服 $kefu = $kefu_data[0]; }else{ //不存在在线客服 //查看机器人设置 $robot = $db1->select('*')->from('tp_kf_robot')->where('storeid = '.$storeid.' and status = 1')->row(); if(!$robot){ //不存在或者机器人未开启 随机匹配不在线客服 发离线消息 $kefu = $db1->query('select * from tp_kf_kefu where storeid='.$storeid.' and Auditing = 1 and is_delete = 0 order by rand() limit 1'); if($kefu) $kefu = $kefu[0]; } } }elseif($config['type'] == 2){ //顺序匹配客服 //没有被客服接待过,顺序匹配客服,若匹配的客服不在线 匹配紧急联系人 $kefu_id = explode(',',$config['match_order']); $next_id = $config['now'] + 1; if(count($kefu_id) <= $next_id){ //说明已到达最后 从新开始 $next_id = 0; } $kefu = $db1->select('*')->from('tp_kf_kefu')->where('id = '.$kefu_id[$next_id])->row(); if($kefu['status'] != 1){ //假如不在线 匹配紧急联系人 if($kefu['second_contact_id']){ $second_contact = $db1->select('*')->from('tp_kf_kefu')->where('id ='.$kefu['second_contact_id'])->row(); if($second_contact && $second_contact['status'] == 1){ //紧急联系人在线 $kefu = $second_contact; } } } //修改config表 下次匹配另外客服 $row_count = $db1->update('tp_kf_config')->cols(array('now'=>$next_id))->where('id = '.$config['id'])->query(); } } //匹配到客服 if($kefu){ //客服 init消息 $log_message1 = [ 'message_type' => 'init', 'data' => [ 'kefu_id' => 'kefu'.$kefu['id'], 'kefu_name' => $kefu['user_name'], 'kefu_avatar' => $kefu['avatar'], 'kefu_sign' => $kefu['sign'], 'kefu_storeid' => $kefu['storeid'], 'kefu_store_name' => $kefu['store_name'], ] ]; Gateway::sendToUid($uid, json_encode($log_message1)); //店铺提示语推送 $store_slogan = $db1->select('sl.slogan,s.store_name,s.storeid,s.avatar')->from('tp_kf_slogan sl')->leftJoin('tp_kf_store s','sl.storeid = s.storeid')->where('sl.storeid = '.$storeid.' AND sl.slogan_status = 1 AND sl.auditing = 1 AND sl.is_delete = 0 AND sl.kefuid = 0')->row(); if($store_slogan){ $log_message2 = [ 'message_type' => 'chatMessage', 'data' => [ 'username' => $store_slogan['store_name'], 'name' => $store_slogan['store_name'], 'id' => 'kefu'.$kefu['id'], 'content' => $store_slogan['slogan'], 'type' => 'friend', 'timestamp' => time() * 1000, 'storeid' => $store_slogan['storeid'], 'avatar' => $store_slogan['avatar'], ] ]; Gateway::sendToUid($uid, json_encode($log_message2)); } //客服提示语推送 $kefu_slogan = $db1->select('sl.slogan,k.user_name,k.storeid,k.avatar')->from('tp_kf_slogan sl')-> leftJoin('tp_kf_kefu k','sl.kefuid = k.id')->where('sl.storeid = '.$storeid.' AND sl.slogan_status = 1 AND sl.auditing = 1 AND sl.is_delete = 0 AND sl.kefuid = '.$kefu['id'])->row(); if($kefu_slogan){ $log_message3 = [ 'message_type' => 'chatMessage', 'data' => [ 'username' => $kefu_slogan['user_name'], 'name' => $kefu_slogan['user_name'], 'id' => 'kefu'.$kefu['id'], 'content' => $kefu_slogan['slogan'], 'type' => 'friend', 'timestamp' => time() * 1000, 'storeid' => $kefu_slogan['storeid'], 'avatar' => $kefu_slogan['avatar'], ] ]; Gateway::sendToUid($uid, json_encode($log_message3)); } //查询最近1周有无需要推送的离线信息 发送历史记录信息 客服发给客户的离线消息 $time = time() - 7 * 3600 * 24; $resMsg = $db1->select('id,from_id,from_name,timeline,content')->from('tp_kf_chatlog') ->where("kefu_id = '{$uid}' and timeline > {$time} and need_send = 1 and from_id = 'kefu{$kefu['id']}'" ) ->query(); if (!empty($resMsg)) { foreach ($resMsg as $key => $vo) { $log_message = [ 'message_type' => 'chatMessage', 'data' => [ 'username' => $vo['from_name'], 'name' => $vo['from_name'], 'id' => $vo['from_id'], 'content' => htmlspecialchars($vo['content']), 'type' => 'friend', 'timestamp' => $vo['timeline'] * 1000, 'avatar' => $kefu['avatar'], ] ]; // sleep(5); Gateway::sendToUid($uid, json_encode($log_message)); //设置推送状态为已经推送 $db1->query("UPDATE `tp_kf_chatlog` SET `need_send` = '0' WHERE id=" . $vo['id']); } } //如果是商家会员 创建客服归属 if(substr($uid,0,2) == 'hy'){ $ownership = $db1->query('select * from tp_kf_ownership where client_id = "'.$uid.'" and store_id = '.$storeid.' and is_delete = 0'); //统计客服归属人数 //$total = $db1->select('count(*) as total')->from('tp_kf_ownership')->where('kefu_id = '.$kefu['id'].' and is_delete = 0')->single(); //|| $total < $config['ownership_person_num'] if(!$ownership){ //不存在 添加数据 $insert_id = $db1->insert('tp_kf_ownership')->cols(array( 'store_id'=> $storeid, 'client_id'=>$uid, 'kefu_id'=> $kefu['id'], 'is_delete'=>0, 'add_time'=> time(), //'del_time'=> time() + $config['ownership_time']*24*60*60, //'status'=> 0 ))->query(); } } }else{ //匹配机器人 // 获取机器人基本信息 $robot = $db1->query('select b.*,s.store_name from tp_kf_robot b LEFT JOIN tp_kf_store s ON b.storeid = s.storeid where b.storeid = '.$storeid); if($robot){ //如果该店铺设置了机器人 机器人提示 $robot = $robot[0]; //机器人信息 $robot_message = [ 'message_type' => 'init', 'data' => [ 'kefu_id' => 'rb'.$robot['id'], 'kefu_name' => $robot['robot_name'], 'kefu_avatar' => $robot['avatar'], 'kefu_sign' => '我是机器人'.$robot['robot_name'].',很高兴为您服务', 'kefu_storeid' => $robot['storeid'], 'kefu_store_name' => $robot['store_name'], ] ]; $robot_menu = $db1->query('select * from tp_kf_attr_question where storeid = '.$storeid.' and pid = 0' ); $html = ''; $html .= '【热门问题】<br />'; $html .= "<pre><span class='rb-menu'>"; foreach ($robot_menu as $k => $v) { $html .= ++$k."、<a href='javascript:;' class='rb-menu-a' style='cursor:pointer;color:blue;' onclick='rbMenu(this)'>".$v['name']."</a><br />"; } $html .= "</span></pre>"; $rbtime = time(); //机器人问候语 $robot_slogan = [ 'message_type' => 'chatMessage', 'data' => [ 'username' => $robot['robot_name'], 'name' => $robot['robot_name'], 'id' => 'rb'.$robot['id'], 'content' =>'您好,我是机器人'.$robot['robot_name'].',很高兴为您服务', 'type' => 'friend', 'timestamp' => $rbtime * 1000, 'avatar' => $robot['avatar'], ] ]; //机器人菜单 $robot_menu = [ 'message_type' => 'chatMessage', 'data' => [ 'username' => $robot['robot_name'], 'name' => $robot['robot_name'], 'id' => 'rb'.$robot['id'], 'content' => $html, 'type' => 'friend', 'timestamp' => $rbtime * 1000, 'avatar' => $robot['avatar'], ] ]; //消息发送 Gateway::sendToUid($uid, json_encode($robot_message)); Gateway::sendToUid($uid, json_encode($robot_slogan)); Gateway::sendToUid($uid, json_encode($robot_menu)); } } } }else{ // 客服离线消息推送 sleep(1); $id = substr($uid,4); //客户设置为已登录IM $db1->query("update `tp_kf_kefu` set status = 1 where id=" . $id); //查询最近1周有无需要推送的离线信息 $time = time() - 7 * 3600 * 24; $resMsg = $db1->select('id,from_id,from_name,timeline,content')->from('tp_kf_chatlog') ->where("kefu_id= '{$uid}' and timeline > {$time} and need_send = 1" ) ->query(); if (!empty($resMsg)) { foreach ($resMsg as $key => $vo) { $log_message = [ 'message_type' => 'chatMessage', 'data' => [ 'username' => $vo['from_name'], 'name' => $vo['from_name'], 'id' => $vo['from_id'], 'content' => $vo['content'], 'type' => 'friend', 'timestamp' => $vo['timeline'] * 1000, 'avatar' => $message['avatar'], ] ]; Gateway::sendToUid($uid, json_encode($log_message)); //设置推送状态为已经推送 $db1->query("UPDATE `tp_kf_chatlog` SET `need_send` = '0' WHERE id=" . $vo['id']); } } } // 通知上线 $online_message = [ 'message_type' => 'online', 'id' => $uid, ]; Gateway::sendToAll(json_encode($online_message)); return; break; case 'chatMessage': $db1 = self::$db; //数据库链接 // 聊天消息 $type = $message['data']['to']['type'] ? $message['data']['to']['type'] : 'friend'; $to_id = $message['data']['to']['id']; $minename = $_SESSION['username']; $toname = $message['data']['to']['username']; $storeid = $message['data']['to']['storeid']; $store_name = empty($message['data']['to']['store_name']) ? '' : $message['data']['to']['store_name']; $uid = $_SESSION['id']; $chat_message = [ 'message_type' => 'chatMessage', 'data' => [ 'username' => $_SESSION['username'], 'name' => $_SESSION['username'], 'avatar' => $_SESSION['avatar'], 'id' => $type === 'friend' ? $uid : $to_id, 'type' => $type, 'content' => htmlspecialchars($message['data']['mine']['content']), 'timestamp' => time() * 1000, ] ]; // 加入聊天log表 $param = [ 'from_id' => $uid, 'kefu_id' => $to_id, 'from_name' => $minename, 'to_name' => $toname, 'content' => htmlspecialchars($message['data']['mine']['content']), 'timeline' => time(), 'storeid' => $storeid, 'store_name' => $store_name, ]; switch ($type) { // 私聊 case 'friend': if (substr($to_id,0,2)=='rb') { // 如果是机器人接待,直接回复 // 机器人回复 if ($message['data']['mine']['content'] == '菜单') { //输入菜单 $robot_menu = $db1->query('select * from tp_kf_attr_question where storeid = '.$storeid.' and pid = 0' ); $html = '<h3>【热门问题】</h3><br />'; $html .= "<pre><span class='rb-menu'>"; foreach ($robot_menu as $k => $v) { $html .= ++$k."、<a href='javascript:;' class='rb-menu-a' style='cursor:pointer;color:blue;' onclick='rbMenu(this)'>".$v['name']."</a><br />"; } $html .= "<br /></span></pre>"; $rbtime = time(); $robot_menu = [ 'message_type' => 'chatMessage', 'data' => [ 'username' => $toname, 'name' => $toname, 'id' => $to_id, 'content' => $html, 'type' => 'friend', 'timestamp' => $rbtime * 1000, 'avatar' => $message['data']['to']['avatar'], ] ]; Gateway::sendToUid($uid, json_encode($robot_menu)); }else { //输入的不是“菜单” // 回复基本逻辑 // 1.先从分类表寻找子分类 // 2.如若找不到子分类则去问题表寻找问题 // 3.问题表找不到问题时则回复:“抱歉 ,没有找到您的相关问题” $attr_id = $db1->query("select id from tp_kf_attr_question where name like '%".$message['data']['mine']['content']."%' and storeid=".$storeid);//取得id if ($attr_id) { //找到客户输入的问题 $ques = $db1->query("select * from tp_kf_attr_question where pid =".$attr_id[0]['id']." and storeid = ".$storeid); if ($ques) { //找到子级问题 $question['content'] = ''; $question['content'] .= '<br /><span style="font-size:26px;color:#06bd38;">答:</span>'.$question['content']; $question['content'] .= '您好,关于“'.$message['data']['mine']['content'].'”我们有以下几点,请您留意关注<br /><br /><hr style="height:1px;border:none;border-top:1px solid #ccc8c8;" /><br />'; $question['content'] .= "<pre><span class='rb-menu'>"; foreach ($ques as $k => $v) { $question['content'] .= ++$k."、<a href='javascript:;' class='rb-menu-a' style='cursor:pointer;color:blue;' onclick='rbMenu(this)'>".$v['name']."</a><br />"; } $question['content'] .= "<br /></span></pre>"; }else { //找答案 $sql = "select * from tp_kf_question where pid =".$attr_id[0]['id']." and storeid=".$storeid; $msg = $db1->query($sql); if (empty($msg)) { //未找到答案 $question['content'] = '<br /><span style="font-size:26px;color:#06bd38;">答:</span>抱歉 ,没有找到您的相关问题1'; }else{ //找到答案 $msg = $msg[0]; $question = $db1->query('select * from tp_kf_answer where que_id='.$msg['id']); $question = $question[0]; // 拼接外部链接 if (!empty($msg['link'])) { $question['content'] .= '。请点击了解<a href="'.$msg['link'].'" target="_blank" style="color:blue;" >更多</a>'; } $question['content'] = '<br /><span style="font-size:26px;color:#06bd38;">答:</span>'.$question['content']; } // 拼接菜单 $ques = $db1->query("select * from tp_kf_attr_question where pid = 0 and storeid = ".$storeid); if ($ques) { $question['content'] .= '<br /><br /><hr style="height:1px;border:none;border-top:1px solid #ccc8c8;" /><br />'; $question['content'] .= '感谢为您服务,您还可以咨询一下问题:<br />'; $question['content'] .= "<pre><span class='rb-menu'>"; foreach ($ques as $k => $v) { $question['content'] .= ++$k."、<a href='javascript:;' class='rb-menu-a' style='cursor:pointer;color:blue;' onclick='rbMenu(this)'>".$v['name']."</a><br />"; } $question['content'] .= "<br /></span></pre>"; } } }else { //未找到客户的问题 $sql = "select * from tp_kf_question where name like '%".$message['data']['mine']['content']."%' and storeid=".$storeid; $msg = $db1->query($sql); if (empty($msg)) { $question['content'] = '<br /><span style="font-size:26px;color:#06bd38;">答:</span>抱歉 ,没有找到您的相关问题'; }else{ $msg = $msg[0]; $question = $db1->query('select * from tp_kf_answer where que_id='.$msg['id']); $question = $question[0]; // 拼接外部链接 if (!empty($msg['link'])) { $question['content'] .= '。请点击了解<a href="'.$msg['link'].'" target="_blank" style="color:blue;" >更多</a>'; } } $ques = $db1->query("select * from tp_kf_attr_question where pid = 0 and storeid = ".$storeid); if ($ques) { $question['content'] .= '<br /><br /><hr style="height:1px;border:none;border-top:1px solid #ccc8c8;" /><br />'; $question['content'] .= '感谢为您服务,您还可以咨询一下问题:<br />'; $question['content'] .= "<pre><span class='rb-menu'>"; foreach ($ques as $k => $v) { $question['content'] .= ++$k."、<a href='javascript:;' class='rb-menu-a' style='cursor:pointer;color:blue;' onclick='rbMenu(this)'>".$v['name']."</a><br />"; } $question['content'] .= "<br /></span></pre>"; } } // 编辑待发送信息 $robot_msg = [ 'message_type' => 'chatMessage', 'data' => [ 'username' => $toname, 'name' => $toname, 'avatar' =>$message['data']['to']['avatar'], 'id' => $to_id, 'type' => 'friend', 'content' => $question['content'], 'timestamp' => time() * 1000, ] ]; Gateway::sendToUid($uid, json_encode($robot_msg)); } }else{ //客服接待 // 插入 if (empty(Gateway::getClientIdByUid($to_id))) { $param['need_send'] = 1; //用户不在线,标记此消息推送 } //聊天记录表 增数据 $db1->insert('tp_kf_chatlog')->cols($param)->query(); Gateway::sendToUid($to_id, json_encode($chat_message)); } break; case 'group': break; } return; break; case 'ping': return; case 'suggest': $db1 = self::$db; //数据库链接 return; case 'addGroup': //客服讨论群组 return; break; case 'addFriend': //添加好友 $client_id = Gateway::getClientIdByUid($message['to_id']);//判断该用户是否在线 //用户在线才通知 if(!empty($client_id)){ $add_message = [ 'message_type' => 'addFriend', 'data' => [ 'username' => $message['username'], 'avatar' => $message['avatar'], 'id' => $message['id'], 'type' => 'friend', 'sign' => $message['sign'], 'groupid' => $message['groupid'], ] ]; Gateway::sendToClient($client_id['0'], json_encode($add_message)); } return; break; //好友申请 case 'applyFriend': $client_id = Gateway::getClientIdByUid($message['to_id']); //用户在线才通知 if(!empty($client_id)){ $apply_message = [ 'message_type' => 'applyFriend', 'data' => [ 'id' => $message['mine_id'] ] ]; Gateway::sendToClient($client_id['0'], json_encode($apply_message)); } return; break; case 'delFriend': //删除好友 $client_id = Gateway::getClientIdByUid($message['to_id']); //用户在线才通知 if(!empty($client_id)){ $add_message = [ 'message_type' => 'delFriend', 'data' => [ 'id' => $message['del_id'] ] ]; Gateway::sendToClient($client_id['0'], json_encode($add_message)); } return; break; case 'online' : //显隐切换 实时在好友面板上灰色与亮色切换 $db1 = self::$db; //数据库链接 // 在线切换状态 $friends = $db1->query("select `user_id` from `tp_kf_friend` where `type` = 1 and `friend_id` = " . substr($message['uid'],4)); if (!empty($friends)) { foreach ($friends as $key => $vo) { $user_client_id = Gateway::getClientIdByUid('kefu'.$vo['user_id']); if (!empty($user_client_id)) { $online_message = [ 'message_type' => ('online' == $message['status']) ? 'online' : 'offline', 'id' => $message['uid'], ]; Gateway::sendToClient($user_client_id['0'], json_encode($online_message)); } } } return; break; default: return; break; } } /** * 当用户断开连接时触发 * @param int $client_id 连接id */ public static function onClose($client_id) { //维持连接的worker退出才触发 $db1 = self::$db; //数据库链接 // 通知下线 $offline_message = [ 'message_type' => 'offline', 'id' => $_SESSION['id'], ]; Gateway::sendToAll(json_encode($offline_message)); if (substr($_SESSION['id'],0,4)=='kefu') { //设置客服用户为退出状态 $id = substr($_SESSION['id'],4); $db1->query("update `tp_kf_kefu` set `status` = 0 where id = " . $id); }else{ //这里可以做客户退出操作,因为IM没有客户表,省略 $id = $_SESSION['id']; //这里可以设置会员客户退出的操作 if(substr($id,0,2) == 'hy'){ //不是游客 $uid = explode('_',$id); if(!empty($uid[1]) && is_numeric($uid[1])){ $db1->query("update `tp_kf_custom` set `status` = 0 where uid = " . $uid[1]); } } } } /** * 写日志 * @param string $world */ public static function setLogs($world = '') { $path = './'; $time = date('Ymd'); $file = fopen($path."$time.txt","a"); $txt = date("Y-m-d H:i:s") .$world."\r\n"; fwrite($file, $txt); fclose($file); } }

 

转载请注明原文地址: https://www.6miu.com/read-5035562.html

最新回复(0)