摘要:我们的平台上有虚拟商品和实体商品两大分类,当时也考虑到了消息的读取状态。商家发送时间是否已读。看前端代码当前的所有代码并不是最终的,目前只是阶段性开发,后期在项目中逐步完善。
前段时间公司提了一个新的需求,在商品的详情页要实现站内买家和商品卖家实时通讯的功能以方便沟通促成交易,要开发此功能当时首先考虑到的就是swoole和workerman了,从网上大概了解了一下关于这两款工具的阐述,功能都是相当强大的,考虑到项目的进度问题,还是选择上手容易比较快的GatewayWorker。
先看一下我们前端设计高大上的模板,分别是用户和卖家后台。 功能还是比较全的,几乎模仿的是QQ。
业务上的大概需求是,用户在进入某个商品详情页下,给用户提供一个和卖家沟通的接口,根据商品的ID找到对应的卖家,类似于淘宝,还有发送图片,发送对应的商品链接;商户后台也差不多。
我们的平台上有虚拟商品和实体商品两大分类,当时也考虑到了消息的读取状态。我的表最初设计如下,没有加任何的索引,考虑的或许也不够周全,有见地的前辈还望指点一二!
DROP TABLE IF EXISTS `hp_chat_log`; CREATE TABLE `hp_chat_log` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT "聊天记录表主键id", `user_id` int(10) unsigned NOT NULL DEFAULT "0" COMMENT "用户id", `merchant_id` varchar(15) COLLATE utf8_unicode_ci NOT NULL DEFAULT "" COMMENT "商家id", `send_message` text COLLATE utf8_unicode_ci NOT NULL, `send_message_type` tinyint(1) NOT NULL DEFAULT "1" COMMENT "发送消息类型(1:普通文本;2:商品链接,3:用户发送图片)", `sender` tinyint(1) NOT NULL DEFAULT "1" COMMENT "发送方。1:用户。2:商家", `send_time` int(11) NOT NULL DEFAULT "0" COMMENT "发送时间", `read_status` tinyint(1) unsigned NOT NULL DEFAULT "0" COMMENT "是否已读。0:未读取。1:已读取", `acc_isonline` tinyint(1) NOT NULL DEFAULT "0" COMMENT "接收方是否在线 (0:不在线;1:在线)", PRIMARY KEY (`id`) ) ENGINE=MyISAM AUTO_INCREMENT=157 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
模板有了,表设计好了,接下来就是搭建服务了,当前项目开发的框架用的是TP5,选择的Websocket框架是GatewayWorker框架,关于GatewayWorker与TP5的整合方法可以看我的这篇文章,讲到了在Linux和
Windows下的整合安装。
http://www.cnblogs.com/wt645631686/p/7219519.html
整合好了之后需要根据当前服务器的一些端口配置在修改一些默认的配置,因为需要客户端通过指定的端口建立连接。
TP5整合好了之后Gateway和workerman的主体目录结构都在TP5的框架目录vendor下的workerman目录下。需要修改里面gateway目录下的一些文件的端口及IP地址配置。
配置完成之后,进入项目目录,按照workerman官方手册提供的使用方法,用命令php start.php start启动socket服务,如以下截图,分别是1238和8282端口。当然可以在后台运行,详细的使用方法请参考手册。
启动好了之后那么就需要在客户端开始下手了,我们项目里是在前端页面里用建立的链接。看前端代码
当前的所有代码并不是最终的,目前只是阶段性开发,后期在项目中逐步完善。
var ws; // 连接服务端 function connect() { // 创建websocket ws = new WebSocket("ws://"+document.domain+":8282"); //当时为了方便以后的维护,这里在php的全局文件里定义了一个常量来定义ip,后来本地开发完提交到linux服务器环境之后发现链接失败! console.log(ws); ws.onopen = onopen; ws.onmessage = onmessage; ws.onclose = function(e) { console.log(e); console.log("连接关闭,定时重连"); connect(); }; ws.onerror = function(e) { console.log(e); console.log("出现错误"); }; } // 握手 function onopen() { var joint = "{"type":"handshake","role":"user"}"; ws.send(joint); } // 服务端发来消息时 function onmessage(e) { var data = JSON.parse(e.data); console.log(data); switch(data["type"]){ // 服务端ping客户端 case "ping": ws.send("{"type":"pong"}"); break; // 登录 更新用户列表 case "handshake": bindUid(data.client_id); $("#client_id").val(data.client_id); break; // 提醒 case "reception": //{"type":"say","from_client_id":xxx,"to_client_id":"all/client_id","content":"xxx","time":"xxx"} warn(data["content"], data["time"], data["timestamp"]); break; } } //绑定uid function bindUid (client_id) { var bindUrl = "{:url("push/push/BindUserClientId")}"; $.post(bindUrl, {client_id: client_id}, function(data){ console.log(data); }, "json"); } //发送连接 function sendLink () { sendTrigger("link"); } // 发送信息 function sendMessage (){ sendTrigger("message"); } function sendTrigger(sendType) { var toMid = $("#toMid").val(); var pid = $("#pid").val(); var message = $("footer .send_content").val(); var client_id = $("#client_id").val(); var sendUrl = "{:url("push/push/SendMessageToMerchant")}"; $.ajax({ url:sendUrl, type:"POST", data:{message:message,toMid:toMid,pid:pid,client_id:client_id,sendType:sendType}, async:false, dataType:"JSON", success:function(data){ data = JSON.parse(data); if (data.status < 0) { alert("发送失败,请稍后再试!"); } else { $("#send_timestamp").val(data.timeStamp); $("#send_timestr").val(data.timeStr); if (sendType == "link") { $("#main").append(data.html); } } } }) } // 提醒 function warn(content, time, prevTmestamp){ var V_image = $("#V_image").val(); var str = "" + timestampWarn(prevTmestamp, time) + "" // $("#main").append(str); // } var t = 0; var i = 0, length = images.localId.length; images.serverId = []; /* upload 方法 -------- start */ function upload() { wx.uploadImage( { localId: images.localId[i], success: function (res) { i++; images.serverId.push(res.serverId); if (i < length) { upload(); } var str = "" $("#main").append(str); if(i >= length ) uploadImageToDb(images.serverId); }, fail: function (res){ } }); } /* upload 方法 -------- end */ upload(); } }) }); function uploadImageToDb(images){ var str = ""; var upUrl = "http://xxxxxx.com/push/push/uploadImgage"; var toMid = $("#toMid").val(); var client_id = $("#client_id").val(); $.post(upUrl,{images:images,toMid:toMid,client_id:client_id},function(data){ if(data == 1){ for(var n = 0 ; n < $(".chat-sender").length ; n++){ str = $(".chat-sender").eq(n).attr("serverId")+","; for(var z=0;z上传失败
后台部分
//微信上传图片 public function uploadImgageAction () { if (!Request::instance()->isPost()) { notFund(); } $images = $_POST["images"]; if (empty($images)) die; $toMid = input("post.toMid", "" , "string"); $client_id = input("post.client_id", "", "string"); if (strlen($client_id) != 20 ) { //客户端错误 return json_encode(["status" => -1]); } if (!is_not_empty_string($toMid)) { //系统错误 return json_encode(["status" => -2]); } require_once dirname(dirname(__FILE__)) . "/Events.php"; $accIsOnline = Gateway::isUidOnline($toMid) == 1 ? 1 : 0; //判读商家是否在线 $message_type = 3; //微信上传图片处理Start $res = json_decode(file_get_contents("access_token.json")); foreach ($res as $key => $value) { if($key == "access_token"){ $access_token = $value; } } $data = []; foreach ($images as $k => $v) { $str = date("YmdHis").rand(1000,9999).".jpg"; $targetName = "./upload/chat/".$str; if (!file_exists("./upload/chat/")) { mkdir("./upload/chat/", 0777, true); } $ch = curl_init("http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=".$access_token."&media_id=".$v); $fp = fopen($targetName, "wb"); curl_setopt($ch, CURLOPT_FILE, $fp); curl_setopt($ch, CURLOPT_HEADER, 0); $msg["status"] = curl_exec($ch); $msg["filename"] = $str; curl_close($ch); fclose($fp); $data[] = $targetName; } //微信上传图片处理End if (!is_not_empty_array($data)) { //微信服务器端图片上传错误 return json_encode(["status" => -2]); } $message = json_encode($data); //Log入库 $insertId = PmodelPush::addChatLog(self::$uid, $toMid, $message, $message_type, 1, $accIsOnline); if ($insertId === false) { //入库失败(服务器故障) return json_encode(["status" => -3]); } $Worker = new Events; $message_json = "{"type":"send","source":"U_" . self::$uid . "","toClientUid":"" . $toMid . "","content":" . $message .", "c_type": " . $message_type .", "Db_id":" . $insertId . "}"; $Worker::onMessage($client_id, $message_json); //成功返回相关数据 return json_encode([ "status" => 1, "timeStamp" => time(), "timeStr" => date("H:i:s") ]); }
其他一些不是很重要的代码就不拿出来了。
当前项目只是一个简单的需求,并没有把GatewayWorker很多强大的功能体现出来,大家以后在项目开发中遇到更为复杂的需求,参考官方手册提供的一些Demo就可以慢慢实现并开发出更为健壮的项目!
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/51738.html
摘要:我们的平台上有虚拟商品和实体商品两大分类,当时也考虑到了消息的读取状态。商家发送时间是否已读。看前端代码当前的所有代码并不是最终的,目前只是阶段性开发,后期在项目中逐步完善。 前段时间公司提了一个新的需求,在商品的详情页要实现站内买家和商品卖家实时通讯的功能以方便沟通促成交易,要开发此功能当时首先考虑到的就是swoole和workerman了,从网上大概了解了一下关于这两款工具的阐述,...
摘要:我们的平台上有虚拟商品和实体商品两大分类,当时也考虑到了消息的读取状态。商家发送时间是否已读。看前端代码当前的所有代码并不是最终的,目前只是阶段性开发,后期在项目中逐步完善。 前段时间公司提了一个新的需求,在商品的详情页要实现站内买家和商品卖家实时通讯的功能以方便沟通促成交易,要开发此功能当时首先考虑到的就是swoole和workerman了,从网上大概了解了一下关于这两款工具的阐述,...
摘要:我们的平台上有虚拟商品和实体商品两大分类,当时也考虑到了消息的读取状态。商家发送时间是否已读。看前端代码当前的所有代码并不是最终的,目前只是阶段性开发,后期在项目中逐步完善。 前段时间公司提了一个新的需求,在商品的详情页要实现站内买家和商品卖家实时通讯的功能以方便沟通促成交易,要开发此功能当时首先考虑到的就是swoole和workerman了,从网上大概了解了一下关于这两款工具的阐述,...
摘要:背景最近在一些项目需要用到实时推送给分组的用户,前端需要传输给后端的信息比较少,通过多方考虑选择了通过框架基于搭建微服务。拥有定时器异步客户端异步异步异步异步消息队列等众多高性能组件。配合的定时器,也可以定时推送数据。 背景 最近在一些项目需要用到Websocket实时推送给分组的用户,前端需要传输给后端的信息比较少,通过多方考虑选择了通过GatewayWorker框架(基于Worke...
摘要:即时通讯中,最重要的是响应速度,我们需要展示消息列表那么这时会有未读消息,未读数量,最后一条消息内容,时间等等。目前设计是单表单库。这里只是对即时通讯设计上做了一些简要的阐述,如有疑问和建议,请在评论区回复。 详解即时通讯设计实现(PHP+GatewayWorker+Redis) 需要实现的功能 一对一聊天(私聊) 一对多聊天(群聊) 类似QQ,微信等聊天列表 实时消息 显示 工具...
阅读 3678·2021-11-24 10:46
阅读 1679·2021-11-15 11:38
阅读 3719·2021-11-15 11:37
阅读 3414·2021-10-27 14:19
阅读 1919·2021-09-03 10:36
阅读 1971·2021-08-16 11:02
阅读 2981·2019-08-30 15:55
阅读 2233·2019-08-30 15:44